オブジェクトのアドレスから hashCode() を計算するにはどうすればよいですか?
質問
Javaにはサブクラスがあります Vertex
Java3D クラスの Point3f
. 。今 Point3f
計算する equals()
座標の値に基づいていますが、私の場合、 Vertex
より厳密にしたいクラス:2 つの頂点が等しいのは、それらが同じオブジェクトである場合のみです。ここまでは順調ですね:
class Vertex extends Point3f {
// ...
public boolean equals(Object other) {
return this == other;
}
}
これが契約に違反していることはわかっています equals()
, ただし、頂点を他の頂点と比較するだけなので、これは問題ではありません。
これで、頂点を HashMap
, 、 hashCode()
メソッドは以下と一致する結果を返さなければなりません equals()
. 。現在はそれを行っていますが、おそらく戻り値は、 Point3f
, したがって、さまざまなハッシュ衝突が発生します。 Vertex
同じ座標を持つオブジェクト。
したがって、私はベースにしたいと思います hashCode()
オブジェクトのアドレスから計算するのではなく、オブジェクトのアドレスに基づいて計算します。 Vertex
さんの畑。私はそれを知っています Object
クラスはこれを行いますが、呼び出すことができません hashCode()
方法 Point3f
それをオーバーライドします。
それで、実際、私の質問は2つあります。
- そんなに浅いものでもいいですか?
equals()
? - はいの場合、ハッシュ コードを計算するためのオブジェクトのアドレスを取得するにはどうすればよいでしょうか?
編集:ちょっと何か思いついたんですが…ランダムを生成できます int
オブジェクト作成時の値を取得し、それをハッシュ コードに使用します。それは良い考えですか?なぜだめですか)?
解決
System.identityHashCode() を使用するか、IdentityHashMap を使用します。
他のヒント
System.identityHashCode()
指定されたオブジェクトに対して、デフォルトのメソッドによって返されるものと同じハッシュ コードを返します。 hashCode()
, 、指定されたオブジェクトのクラスがオーバーライドされるかどうか hashCode()
.
これでもデリゲートを使用します 答え おそらく良いでしょう。
class Vertex extends Point3f{
private final Object equalsDelegate = new Object();
public boolean equals(Object vertex){
if(vertex instanceof Vertex){
return this.equalsDelegate.equals(((Vertex)vertex).equalsDelegate);
}
else{
return super.equals(vertex);
}
}
public int hashCode(){
return this.equalsDelegate.hashCode();
}
}
参考までに、あなたのequalsメソッドはequalsコントラクト(つまり、基本オブジェクトのコントラクト)に違反しません...これは基本的に、ベース Object メソッドの equals メソッドであるため、Vertexequals の代わりに Identityequals が必要な場合は、それで問題ありません。
ハッシュ コードに関しては、実際には変更する必要はありませんが、受け入れられた答えは良い選択肢であり、ハッシュ テーブルに同じ値を持つ頂点キーが多数含まれている場合ははるかに効率的になります。
変更する必要がない理由は、ハッシュ コードが false を返すオブジェクトに対して同じ値を返すのはまったく問題ないためです。すべてのインスタンスに対して常に 0 を返すだけの有効なハッシュ コードですらあります。これがハッシュテーブルにとって効率的であるかどうかは、まったく別の問題です...多くのオブジェクトが同じハッシュ コードを持つ場合、より多くの衝突が発生します (これは、ハッシュ コードをそのままにして、同じ値を持つ頂点が多数ある場合に当てはまる可能性があります)。
もちろん、これを答えとして受け入れないでください (あなたが選んだものははるかに実用的です)。ハッシュ コードと等号に関する背景情報をもう少し説明したかっただけです ;-)
そもそもなぜ hashCode() をオーバーライドしたいのでしょうか?他の平等の定義を使用したい場合は、そうすることをお勧めします。例えば
パブリッククラスA {int id;
public boolean equals(a other){return other.id == id} public int hashcode(){return id;}
} IDが同じである場合、オブジェクトは同じであり、ハッシュコードをオーバーライドしてこれを行うことができないことを明確にしたい場合:
HashSet ハッシュ = 新しい HashSet();hash.add(新しいA(1));hash.add(新しいA(1));そして、(等価性の定義の観点から)同一の 2 つの A を取得します。正しい動作は、ハッシュ内にオブジェクトが 1 つだけあり、2 回目の書き込みで上書きされることになります。
論理比較として等号を使用しているのではなく、物理比較として等号を使用しているため(つまり、同じオブジェクトです)、ハッシュコードが一意の値を返すことを保証する唯一の方法は、独自の提案のバリエーションを実装することです。乱数を生成する代わりに、UUID を使用して各オブジェクトの実際の一意の値を生成します。
System.identityHashCode() は機能します。 ほとんど 現時点では有効ですが、Object.hashCode() メソッドは次のとおりであるため保証されません。 ない すべてのオブジェクトに対して一意の値を返すことが保証されています。私は、限界的なケースが起こるのを見てきましたが、それはおそらく VM 実装に依存するでしょう。これはコードに依存させたくないものです。
Object.hashCode() の Javadoc からの抜粋:合理的に実用的である限り、クラス Object によって定義された hashCode メソッドは、個別のオブジェクトに対して個別の整数を返します。(これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法は JavaTM プログラミング言語では必要ありません。)
これが解決する問題は、2 つの別個のポイント オブジェクトが同じハッシュを持っているため、ハッシュマップに挿入されたときに相互に上書きされない場合です。論理的に等しいものがないため、hashCode() のオーバーライドを伴うと、identityHashCode メソッドによって実際にこのシナリオが発生する可能性があります。論理的な場合は同じ論理ポイントのハッシュ エントリのみを置き換える場合、システム ベースのハッシュを使用すると、任意の 2 つのオブジェクトでそれが発生する可能性があり、等価性 (さらにはクラス) は要素ではなくなります。
関数 hashCode() は Object から継承され、意図したとおりに機能します (座標レベルではなくオブジェクト レベルで)。変更する必要はありません。
あなたのequalsメソッドに関しては、コード内でequalsを使用する代わりにobj1 == obj2を実行するだけで済むため、それを使用する理由さえありません。これは、座標の比較がはるかに意味のあるソートなどを目的としているためです。