hashCode() メソッドがオーバーライドされていない場合、Java の任意のオブジェクトで hashCode() を呼び出した結果はどうなりますか?
12 に答える
HotSpot JVM では、デフォルトで、非オーバーロードObject.hashCode
またはSystem.identityHashCode
乱数の最初の呼び出しで生成され、オブジェクト ヘッダーに格納されます。後続の呼び出しは、ヘッダーからこの値を抽出するObject.hashCode
か、単に抽出します。System.identityHashCode
デフォルトでは、オブジェクトのコンテンツやオブジェクトの場所とは何の共通点もなく、ただの乱数です。この動作は-XX:hashCode=n
、次の可能な値を持つ HotSpot JVM オプションによって制御されます。
- 0: グローバル ランダム ジェネレーターを使用します。これは Java 7 のデフォルト設定です。複数のスレッドからの同時呼び出しによって競合状態が発生し、異なるオブジェクトに対して同じ hashCode が生成されるという欠点があります。また、高度な同時実行環境では、競合が原因で遅延が発生する可能性があります (異なる CPU コアから同じメモリ領域を使用する)。
- 5: 以前の欠点のないスレッド ローカルな xor シフト ランダム ジェネレーターを使用します。これは Java 8 のデフォルト設定です。
- 1: 「stop-the-world」イベントで変更されるランダムな値と混合したオブジェクト ポインターを使用するため、stop-the-world イベント間 (ガベージ コレクションなど) で生成された hashCode は安定しています (テスト/デバッグの目的で)。
- 2: 常に使用
1
(テスト/デバッグ目的) - 3: 自動インクリメント番号を使用します (テスト/デバッグの目的で、グローバル カウンターも使用されるため、競合や競合状態が発生する可能性があります)
- 4: 必要に応じて 32 ビットにトリミングされたオブジェクト ポインターを使用します (テスト/デバッグの目的で)。
-XX:hashCode=4
を設定しても、hashCode が常にオブジェクト アドレスを指すとは限らないことに注意してください。オブジェクトは後で移動できますが、hashCode は同じままです。また、オブジェクト アドレスの分散が不十分なため (アプリケーションがあまりメモリを使用しない場合、ほとんどのオブジェクトは互いに近くに配置されます)、このオプションを使用するとハッシュ テーブルのバランスが崩れる可能性があります。
通常、 hashCode() は、オーバーライドしない場合、メモリ内のオブジェクトのアドレスを返すだけです。
1から:
合理的に実用的である限り、クラス Object によって定義された hashCode メソッドは、個別のオブジェクトに対して個別の整数を返します。(これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法は JavaTM プログラミング言語では必要ありません。)
の実装はhashCode()
クラスごとに異なる場合がありますが、 の契約は非常に具体的であり、 JavadocshashCode()
に明確かつ明示的に記載されています。
オブジェクトのハッシュ コード値を返します。このメソッドは、java.util.Hashtable によって提供されるハッシュテーブルなどの利点のためにサポートされています。
hashCode の一般的な契約は次のとおりです。
- Java アプリケーションの実行中に同じオブジェクトに対して複数回呼び出された場合は常に、オブジェクトの equals 比較で使用される情報が変更されていない限り、hashCode メソッドは一貫して同じ整数を返す必要があります。この整数は、あるアプリケーションの実行から同じアプリケーションの別の実行まで一貫性を保つ必要はありません。
- equals(Object) メソッドに従って 2 つのオブジェクトが等しい場合、2 つのオブジェクトのそれぞれで hashCode メソッドを呼び出すと、同じ整数結果が生成される必要があります。
- equals(java.lang.Object) メソッドに従って 2 つのオブジェクトが等しくない場合、2 つのオブジェクトのそれぞれで hashCode メソッドを呼び出すと、異なる整数結果が生成される必要はありません。ただし、プログラマーは、等しくないオブジェクトに対して個別の整数結果を生成すると、ハッシュテーブルのパフォーマンスが向上する可能性があることに注意する必要があります。
合理的に実用的である限り、クラス Object によって定義された hashCode メソッドは、個別のオブジェクトに対して個別の整数を返します。(これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法は JavaTM プログラミング言語では必要ありません。)
hashCode()
は と密接に結びついてequals()
おり、オーバーライドequals()
する場合は もオーバーライドする必要がありますhashCode()
。
ハッシュコードがオーバーライドされていない場合は、オブジェクトのハッシュコードを呼び出します。これは、javadoc からの抜粋です。
合理的に実用的である限り、クラス Object によって定義された hashCode メソッドは、個別のオブジェクトに対して個別の整数を返します。(これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法は JavaTM プログラミング言語では必要ありません。)
デフォルトのハッシュコード実装は、jvm 内のオブジェクトの内部アドレスを 32 ビット整数として提供します。したがって、2 つの異なる (メモリ内の) オブジェクトは、異なるハッシュコードを持ちます。
これは equals のデフォルトの実装と一致しています。オブジェクトの equals をオーバーライドする場合は、hashCode を適合させて一貫性を持たせる必要があります。
概要については、http://www.ibm.com/developerworks/java/library/j-jtp05273.htmlを参照してください。
6桁の16進数を返します。これは通常、オブジェクトがアドレス指定されるスロットのメモリ位置です。アルゴリズム自体からすると、JDKはオープンアドレッシングに最適なハッシュ関数の1つであるダブルハッシュ(ネイティブ実装)を実行していると思います。このダブルハッシュスキームは、衝突の可能性を大幅に減らします。
次の投稿は支持的なアイデアを与えるでしょう-
本当に答えではありませんが、以前のコメントに追加します
オブジェクトの内部アドレスが JVM 内で変更されないままであることは保証できず、そのガベージ コレクターはヒープの圧縮中にオブジェクトを移動する可能性があります。
私はこのようなことをしようとしました:
public static void main(String[] args) {
final Object object = new Object();
while (true) {
int hash = object.hashCode();
int x = 0;
Runtime r = Runtime.getRuntime();
List<Object> list = new LinkedList<Object>();
while (r.freeMemory() / (double) r.totalMemory() > 0.3) {
Object p = new Object();
list.add(p);
x += object.hashCode();//ensure optimizer or JIT won't remove this
}
System.out.println(x);
list.clear();
r.gc();
if (object.hashCode() != hash) {
System.out.println("Voila!");
break;
}
}
}
しかし、実際にはハッシュコードは変わりません... Sun の JDK が実際にどのように Obect.hashcode を実装しているか教えてもらえますか?
オブジェクトごとに異なる結果が得られるように、ハッシュ コードの実装を試みる必要があります。これを行う標準的な方法はないと思います。
詳細については、この記事をお読みください。
ハッシュコードは、ハッシュセットなどのコレクションにオブジェクトを格納するのに役立ちます。オブジェクトがハッシュコードを一意のものとして定義できるようにすることで、HashSet のアルゴリズムを効果的に機能させることができます。
オブジェクト自体はメモリ内のオブジェクトのアドレスを使用します。これは非常に一意ですが、2 つの異なるオブジェクト (たとえば 2 つの同一の文字列) を同じと見なす必要がある場合は、それらがメモリ内で複製されている場合でも、あまり役に立ちません。
equals() に関して、異なるハッシュ コードを持つ 2 つのオブジェクトが等しくなってはなりません
a.hashCode() != b.hashCode()
暗示する必要があります!a.equals(b)
ただし、 equals() に関して等しくない 2 つのオブジェクトは、同じハッシュ コードを持つことができます。これらのオブジェクトをセットまたはマップに格納すると、多くのオブジェクトが同じハッシュ コードを持つ場合、効率が低下します。