最近これを読みました開発者作業ドキュメント。
このドキュメントではhashCode()
、 をequals()
効果的かつ正確に定義することについて説明していますが、なぜこれら 2 つのメソッドをオーバーライドする必要があるのかがわかりません。
これらの方法を効率的に実装するにはどうすればよいでしょうか?
ベストアンサー1
ジョシュア・ブロックはEffective Javaについてこう語る
equals() をオーバーライドするすべてのクラスで hashCode() をオーバーライドする必要があります。オーバーライドしないと、Object.hashCode() の一般規約に違反することになり、HashMap、HashSet、Hashtable などのすべてのハッシュベースのコレクションと組み合わせてクラスが適切に機能しなくなります。
equals()
オーバーライドせずに をオーバーライドしhashCode()
、 を使用しようとした場合に何が起こるかの例を使用して理解してみましょうMap
。
このようなクラスがあり、 の 2 つのオブジェクトは、が等しい場合 ( Eclipse によって生成され、の場合) にMyClass
等しいとします。importantField
hashCode()
equals()
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
これを持っていると想像してください
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
上書きのみequals
のみがequals
オーバーライドされる場合、最初に を呼び出すと はmyMap.put(first,someValue)
何らかのバケットにハッシュされ、 を呼び出すとmyMap.put(second,someOtherValue)
別のバケットにハッシュされます ( が異なるためhashCode
)。 したがって、それらは等しいものの、同じバケットにハッシュされないため、マップはそれを認識できず、両方ともマップ内に残ります。
equals()
をオーバーライドする場合は をオーバーライドする必要はありませんが、 の 2 つのオブジェクトが等しい場合に が等しいことがわかっているのに をオーバーライドしないというhashCode()
特定のケースで何が起こるかを見てみましょう。MyClass
importantField
equals()
上書きのみhashCode
オーバーライドのみの場合はhashCode
、呼び出し時にmyMap.put(first,someValue)
最初に取得し、計算してhashCode
指定されたバケットに格納します。その後、呼び出し時に、myMap.put(second,someOtherValue)
最初の値を2番目の値に置き換えます。マップドキュメントこれらは等しいからです (ビジネス要件によると)。
しかし、問題は、equals が再定義されていないため、マップがsecond
バケットをハッシュして反復処理し、が truek
であるオブジェクトがあるかどうかを調べるときに、 となるsecond.equals(k)
オブジェクトが見つからないことです。second.equals(first)
false
分かりやすくなったと思います