汎用オブジェクトのハッシュ関数をどのように思いつきますか? ユーザーによって定義されたように「等しい」場合、2 つのオブジェクトは同じハッシュ値を持つ必要があるという制約があります。Java はこれをどのように実現しますか?
6 に答える
まず、基本的に、 hashCode() メソッドをオーバーライドして、クラスのハッシュ関数を定義します。Javadoc には次のように記載されています。
hashCode の一般的な契約は次のとおりです。
- Java アプリケーションの実行中に同じオブジェクトに対して複数回呼び出された場合は常に、オブジェクトの equals 比較で使用される情報が変更されていない限り、hashCode メソッドは一貫して同じ整数を返す必要があります。この整数は、あるアプリケーションの実行から同じアプリケーションの別の実行まで一貫性を保つ必要はありません。
- equals(Object) メソッドに従って 2 つのオブジェクトが等しい場合、2 つのオブジェクトのそれぞれで hashCode メソッドを呼び出すと、同じ整数結果が生成される必要があります。
- equals(java.lang.Object) メソッドに従って 2 つのオブジェクトが等しくない場合、2 つのオブジェクトのそれぞれで hashCode メソッドを呼び出すと、異なる整数結果が生成される必要はありません。ただし、プログラマは、等しくないオブジェクトに対して個別の整数結果を生成すると、ハッシュ テーブルのパフォーマンスが向上する可能性があることに注意する必要があります。
したがって、より重要な質問は次のとおりです。2 つのオブジェクトが等しい理由は何ですか? またはその逆: オブジェクトをユニークにするプロパティは何ですか? それに対する答えがある場合は、すべてのプロパティを比較し、それらがすべて同じかどうかを返すequals()メソッドを作成します。true
false
hashCode()
メソッドはもう少し複雑です。自分で作成するのではなく、IDE に任せることをお勧めします。Eclipse では、[ソース]を選択し、メニューからhashCode() と equals() を生成します。これにより、上記の要件が満たされることも保証されます。
以下は、Eclipse を使用して 2 つのメソッドが生成された小さな (そして簡略化された) 例です。はすでに国内の都市を一意に識別しているcity
ため、プロパティを含めないことを選択したことに注意してください。zipCode
public class Address {
private String streetAndNumber;
private String zipCode;
private String city;
private String country;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((country == null) ? 0 : country.hashCode());
result = prime * result
+ ((streetAndNumber == null) ? 0 : streetAndNumber.hashCode());
result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if(this == obj)
return true;
if(obj == null)
return false;
if(!(obj instanceof Address))
return false;
final Address other = (Address) obj;
if(country == null) {
if(other.country != null)
return false;
}
else if(!country.equals(other.country))
return false;
if(streetAndNumber == null) {
if(other.streetAndNumber != null)
return false;
}
else if(!streetAndNumber.equals(other.streetAndNumber))
return false;
if(zipCode == null) {
if(other.zipCode != null)
return false;
}
else if(!zipCode.equals(other.zipCode))
return false;
return true;
}
}
私は自分の質問に対する答えを見つけました。Java が行う方法は、すべてのオブジェクトに対して hashCode を定義することであり、デフォルトでは、2 つのオブジェクトがメモリ内で同じである場合、2 つのオブジェクトの hashCode は同じです。したがって、ハッシュテーブルのクライアントがオブジェクトの equals() メソッドをオーバーライドする場合、a.equals(b) が true の場合、a.hashCode() も b.hashCode と等しくなければならないなど、ハッシュコードを計算するメソッドもオーバーライドする必要があります。 (). このようにして、等しいオブジェクトが同じハッシュコードを持つことが保証されます。
Javaはそれをしません。hashCode() と equals() が明示的に実装されていない場合、JVM は意味のある等しいインスタンスに対して異なる hashCode を生成します。Joshua Bloch による効果的な Java を確認できます。本当に助かります。
クラスはjava.lang.Object
ごまかします。等式 ( によって決定されるequals
) をオブジェクト ID ( によって決定できる) として定義し==
ます。したがって、equals
サブクラスでオーバーライドしない限り、同じオブジェクトである場合、クラスの 2 つのインスタンスは「等しい」ことになります。
これに関連付けられたハッシュ コードは、システム関数によって実装されますSystem.identityHashCode
(これは、もはや実際にはオブジェクト アドレスに基づいていませんが、このように実装されていると考えることができます)。
をオーバーライドequals
すると、 のこの実装hashCode
は意味をなさなくなります。
次の例を検討してください。
class Identifier {
private final int lower;
private final int upper;
public boolean equals(Object any) {
if (any == this) return true;
else if (!(any instanceof Identifier)) return false;
else {
final Identifier id = (Identifier)any;
return lower == id.lower && upper == id.upper;
}
}
}
このクラスの 2 つのインスタンスは、「下位」メンバーと「上位」メンバーの値が同じである場合、等しいと見なされます。hashCode
等価性はオブジェクト メンバーによって決定されるようになったため、互換性のある方法で定義する必要があります。
public int hashCode() {
return lower * 31 + upper; // possible implementation, maybe not too sophisticated though
}
ご覧のとおり、hashCode
同等性を判断するときにも使用するのと同じフィールドを使用します。一般に、等しいかどうかを比較するときにも考慮されるすべてのメンバーに基づいてハッシュ コードを作成することをお勧めします。
代わりに、次の例を検討してください。
class EmailAddress {
private final String mailbox;
private final String displayName;
public boolean equals(Object any) {
if (any == this) return true;
else if (!(any instanceof EmailAddress)) return false;
else {
final EmailAddress id = (EmailAddress)any;
return mailbox.equals(id.mailbox);
}
}
}
ここでは、等価性はmailbox
メンバーによってのみ決定されるため、ハッシュ コードもそのメンバーのみに基づく必要があります。
public int hashCode() {
return mailbox.hashCode();
}
いくつかのオプション:
- Joshua Bloch 著『Effective Java』を読んでください。ハッシュコードの優れたアルゴリズムが含まれています
- IDE に hashCode メソッドを生成させる
- Java SE 7 以降: Objects.hashを使用
オブジェクトのハッシュは、開発者がオーバーライドできるhashCode()
メソッドをオーバーライドすることによって確立されます。
Java は、デフォルトのハッシュコード計算で素数を使用します。
equals()
andメソッドが実装されていない場合hashCode()
、JVM はオブジェクトのハッシュコードを暗黙的に生成します (Serializable クラスの場合は aserialVersionUID
が生成されます)。