Object#equals()を実装する。
だいぶ前に書いた、オブジェクト比較ユーティリティを使っています。以下はサンプル。
class Kitten { // フィールド private String name; private int age; private Kitten[] friends; public boolean equals ( Object obj ) { if ( obj == null ) { return false; } if ( obj instanceof Kitten ) { Object[] that = getValues((Kitten) obj); return CompareUtil.equalsSeq(that, getValues(this)); // 親クラスを持つ場合はそちらも比較する。 // return CompareUtil.equalsSeq(that, getValues(this)) // && super.equals( obj ); } return false; } public int hashCode () { return CompareUtil.hashCodeSeq(getValues(this)); // 親クラスを持つ場合 // return 31 * CompareUtil.hashCodeSeq(getValues(this)) // + super.hashCode(); } // 比較するフィールドを返す関数 // equals(),hashcode()の比較対象をここに集約する。 private static Object[] getValues(Kitten v) { return new Object[] { // 比較するフィールド値をここに並べる。 v.name, // プリミティブ型はObjectに。JDK5からは不要。 new Integer( v.age ), // 配列はListやSetにする。 // 出現順を考慮する場合、List。未考慮でよい場合、Set。 // friendsは順番未考慮でよいのでSet。 CompareUtil.toSet( v.friends ) }; } }
比較するフィールドを関数に集約しているので、フィールドを追加した場合の修正が簡単なのがメリット。比較対象フィールドは自分で明示的に設定します。Bean APIで比較対象フィールドを探すという手もありますが、以下を考慮して泥臭いアプローチを採用。
- 柔軟性 : 比較対象への追加、削除が簡単にできる。
- 性能: リフレクションAPIは重そう。
ユーティリティの実装は次の通りです。
import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * {@link Object}の比較に関する基本メソッドの実装を提供する * ユーティリティメソッドライブラリ。 */ public final class CompareUtil { /** * 順番を考慮してハッシュコードを計算する。 * @param vals フィールド値 * @return ハッシュコード */ public static final int hashCodeSeq(Object[] vals) { int hashCode = 1; for (int i = vals.length -1; i >= 0; i-- ) { hashCode = 31 * hashCode + (vals[i] == null ? 0: vals[i].hashCode()); } return hashCode; } /** * 順番未考慮でハッシュコードを計算する。 * @param vals フィールド値 * @return ハッシュコード */ public static final int hashCode(Object[] vals) { int hashCode = 1; for (int i = vals.length -1; i >= 0; i-- ) { hashCode = hashCode + (vals[i] == null ? 0 : vals[i].hashCode()); } return hashCode; } /** * 順番を考慮して配列を比較する。<br/> * <ul> * <li>各配列における要素の出現順を考慮する。</li> * <li>要素数を考慮する。</li> * </ul> * @param a 比較対象 * @param b 比較対象 * @return 同じであればtrue */ public static final boolean equalsSeq(Object[] a, Object[] b) { //数を比較 if (a.length != b.length) { return false; } //値を比較 for (int i = b.length -1; i >= 0; i-- ) { if (!(a[i] != null ? a[i].equals(b[i]) : b[i] == null)) { return false; } } return true; } /** * 順番未考慮で配列を比較する。<br/> * <ul> * <li>各配列における要素の出現順は未考慮。</li> * <li>要素数を考慮する。</li> * <li>各配列中の同一の要素は1つと見なす。</li> * </ul> * @param a 比較対象 * @param b 比較対象 * @return 同じであればtrue */ public static final boolean equals(Object[] a, Object[] b) { //数を比較 if (a.length != b.length) { return false; } //値を比較 Set sa = toSet(a); Set sb = toSet(b); boolean res = sa.equals(sb); return res; } /** * 比較のため、配列を{@link Set}に格納する。 * @param a 配列 * @return 配列を格納した{@link Set} */ public final static Set toSet(Object[] a) { if ( a == null ) { return null; } Set set = new HashSet(); for (int i = 0; i < a.length; i++) { set.add(a[i]); } return set; } /** * 比較のため、配列を{@link List}に格納する。 * @param a 配列 * @return 配列を格納した{@link List} */ public final static List toList(Object[] a) { if ( a == null ) { return null; } List list = new ArrayList(); for (int i = 0; i < a.length; i++) { list.add(a[i]); } return list; } }