2009-04-13 6 views
39

これは奇妙です。同僚がjavaのmyArray.hashCode()の実装について質問しました。私は知っていると思ったが、その後いくつかのテストを行った。以下のコードを確認してください。奇妙なことに気付いたのは、私が最初のシステムを書いたときに結果が異なっていたということです。それはメモリアドレスを報告しているようなもので、クラスを変更するとアドレスや何かを移動したようなものです。ちょうど私が共有すると思った。Java ArrayのHashCode実装

int[] foo = new int[100000]; 
java.util.Random rand = new java.util.Random(); 

for(int a = 0; a < foo.length; a++) foo[a] = rand.nextInt(); 

int[] bar = new int[100000]; 
int[] baz = new int[100000]; 
int[] bax = new int[100000]; 
for(int a = 0; a < foo.length; a++) bar[a] = baz[a] = bax[a] = foo[a]; 

System.out.println(foo.hashCode() + " ----- " + bar.hashCode() + " ----- " + baz.hashCode() + " ----- " + bax.hashCode()); 

// returns 4097744 ----- 328041 ----- 2083945 ----- 2438296 
// Consistently unless you modify the class. Very weird 
// Before adding the comments below it returned this: 
// 4177328 ----- 4097744 ----- 328041 ----- 2083945 


System.out.println("Equal ?? " + 
    (java.util.Arrays.equals(foo, bar) && java.util.Arrays.equals(bar, baz) && 
    java.util.Arrays.equals(baz, bax) && java.util.Arrays.equals(foo, bax))); 

答えて

77

java.lang.ArrayhashCode方法はハッシュコードを基準に依存することを意味する、Objectから継承されています。配列の内容に基づいてハッシュコードを取得するには、Arrays.hashCodeを使用します。

浅いハッシュコード実装ですが、注意してください。深い実装も存在しますArrays.deepHashCode

+1

感謝を参照してくださいが、 java.lang.Arrayは、デフォルトでhashCode(およびtoString)メソッドをオーバーライドしないのはなぜですか?何か良い理由はありますか? –

+4

hashCodeは高速である必要があり(大部分は高価な.equalsの呼び出しを防ぐために使用されるため)、配列上の浅い値のhashCodeさえも非常に遅い可能性があります。基本的にランダムなhashCodeは害を与えません。ただ利点がありません。 2つの悪のうちの小さい方。 – Torque

4

アレイでは、メモリの場所に基づいてデフォルトのハッシュコードが使用されます(ただし、のメモリの場所はintであり、すべてのメモリアドレスが適合しないため)。これは、System.identityHashCode(foo)の結果を印刷することでもわかります。

アレイは同じで同一の配列の場合にのみequalです。したがって、配列のハッシュコードは、同じで同じ配列の場合にのみ、一般に等しくなります。

+0

(オブジェクトはメモリ内で移動され、ハッシュコードを見ると通常はアドレスのようには見えません) –

2

実際には、実装に依存しますが、Object.hashCode()のデフォルトの実装は実際にオブジェクトのポインタ値を返します。例えば、64ビットJVMは、ポインタとXORと上位ワードと下位ワードを一緒に取ることができます。サブクラスは、この動作を意味がある場合はオーバーライドすることをお勧めします。

しかし、突然変異可能な配列に対して等価比較を行うことは意味がありません。要素が変更された場合、その2つはもはや等しくなりません。その配列に何が起こっても同じ配列が常に同じhashCodeを返す不変条件を維持するために、配列はデフォルトのハッシュコードの振る舞いをオーバーライドしません。

java.util.Arraysは、配列自体のアイデンティティではなく、配列の内容に基づくハッシングが重要な場合のdeepHashCode()実装を提供することに注意してください。

+1

現代のVMはオブジェクトをメモリ内で移動します。現在のアドレスはシードとして使用できますが、結果を保存する必要があります。 –

+1

メモリ内を移動しても、hashCodeは変更されません。 –

2
私はjava.util.Arrays.hashCode(またはGoogleのグアバ汎用ラッパーObjects.hashcode)を使用して同意するが、あなたはテラコッタを使用している場合、これは問題を引き起こす可能性があることに注意してください

- この答えのためのthis link

関連する問題