自前のクラスをArrayListなどに入れてまとめて扱うことは多々あると思いますが、
同値判定にかかわる部分ついて押さえておかないと、
期待したとおりの動作にならずつまづくことがあるのでメモしておきます。
サンプルのソースでは呼び出し側のMyClassで、自作したMyComparableClassのインスタンスを各コレクションクラスに挿入し、
別のインスタンスを使って「既に存在するか」のチェックをさせます。
結果は一番下に示した様になります。
判定に使われるメソッドについてまとめると以下のようになります。
(1)ArrayListとLinkedList
equalsが呼ばれる。
(2)HashMapとHashSet
hashCodeが呼ばれ、同値だった場合にequalsが呼ばれる。
(3)TreeMapとTreeSet
compareToが呼ばれる。
equalsとcompareToの関係についてはこちらで詳しく解説されています。
「equalsとcompareToが一貫性してることは強く推奨されるけど、BigDecimalは例外」だそうな。
■自前のクラス MyComparableClass
/**
* Comparableを実装したクラス
*/
public class MyComparableClass<T extends Comparable<T>> implements Comparable<MyComparableClass<T>> {
/** フィールド1 */
private T field1;
/**
* フィールド1を返す
* @return field1
*/
public T getField1() {
return field1;
}
/**
* コンストラクタ
* @param field1 フィールド1
*/
public MyComparableClass(T field1) {
this.field1 = field1;
}
/**
* このオブジェクトと指定されたオブジェクトの順序を比較します。
* @param other
* @return
*/
@Override
public int compareTo(MyComparableClass<T> other) {
System.out.println("compareToが呼ばれました:" + field1.toString());
return field1.compareTo(other.getField1());
}
/**
* オブジェクトのハッシュ・コード値を返します。
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
System.out.println("hashCodeが呼ばれました:" + field1.toString());
final int prime = 31;
int result = 1;
result = prime * result + ((field1 == null) ? 0 : field1.hashCode());
return result;
}
/**
* このオブジェクトと他のオブジェクトが等しいかどうかを示します。
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
System.out.println("equalsが呼ばれました:" + field1.toString());
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("unchecked")
MyComparableClass<T> other = (MyComparableClass<T>) obj;
if (field1 == null) {
if (other.field1 != null)
return false;
} else if (!field1.equals(other.field1))
return false;
return true;
}
}
■呼び出し側(MyClass)
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
public class MyClass {
public static void main(String... args) {
MyComparableClass<BigDecimal> val1 = new MyComparableClass<>(new BigDecimal("100"));
MyComparableClass<BigDecimal> val2 = new MyComparableClass<>(new BigDecimal("100.00"));
System.out.println("-----ArrayList---------------");
List<MyComparableClass<BigDecimal>> myArrayList = new ArrayList<>();
myArrayList.add(val1);
if (myArrayList.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
System.out.println("-----LinkedList---------------");
List<MyComparableClass<BigDecimal>> myLinkedList = new LinkedList<>();
myLinkedList.add(val1);
if (myLinkedList.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
System.out.println("-----HashMap---------------");
Map<MyComparableClass<BigDecimal>, Integer> myHashMap = new HashMap<>();
myHashMap.put(val1, 0);
if (myHashMap.containsKey(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
System.out.println("-----TreeMap---------------");
Map<MyComparableClass<BigDecimal>, Integer> myTreeMap = new TreeMap<>();
myTreeMap.put(val1, 0);
if (myTreeMap.containsKey(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
System.out.println("-----HashSet---------------");
Set<MyComparableClass<BigDecimal>> myHashSet = new HashSet<>();
myHashSet.add(val1);
if (myHashSet.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
System.out.println("-----TreeSet---------------");
Set<MyComparableClass<BigDecimal>> myTreeSet = new TreeSet<>();
myTreeSet.add(val1);
if (myTreeSet.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
}
}
■実行結果
-----ArrayList---------------
equalsが呼ばれました:100.00
>>ng
-----LinkedList---------------
equalsが呼ばれました:100.00
>>ng
-----HashMap---------------
hashCodeが呼ばれました:100
hashCodeが呼ばれました:100.00
>>ng
-----TreeMap---------------
compareToが呼ばれました:100
compareToが呼ばれました:100.00
>>ok
-----HashSet---------------
hashCodeが呼ばれました:100
hashCodeが呼ばれました:100.00
>>ng
-----TreeSet---------------
compareToが呼ばれました:100
compareToが呼ばれました:100.00
>>ok