JavaでhashCode()
メソッドが返す値は何ですか?
オブジェクトのメモリ参照であると読みました... new Integer(1)
のハッシュ値を出力すると1です。 String("a")
は97です。
私は混乱しています:ASCIIまたは値の種類は何ですか?
ハッシュコードは、呼び出されたオブジェクトの状態を表す整数値です。 Integer
が1に設定されている場合、Integer's
ハッシュコードとその値は同じものです。文字のハッシュコードは、ASCII文字コードです。カスタムタイプを記述する場合、現在のインスタンスの状態を最もよく表す適切なhashCode
実装を作成する必要があります。 。
hashCode()
によって返される値は、オブジェクトのメモリアドレスであることを保証するものではありません。 Object
クラスでの実装についてはわかりませんが、ほとんどのクラスはhashCode()
をオーバーライドするため、意味的に等価な(ただし同じインスタンスではない)2つのインスタンスは同じ値にハッシュします。これは、hashCode
と一貫性のあるequals
に依存するSetなどの別のデータ構造内でクラスを使用できる場合に特に重要です。
オブジェクトのインスタンスを一意に識別するhashCode()
はありません。基礎となるポインターに基づいたハッシュコード(たとえば、Sunの実装)が必要な場合は、System.identityHashCode()
を使用します-これは、オーバーライドされたかどうかに関係なく、デフォルトのhashCode
メソッドに委任します。
それでも、System.identityHashCode()
でさえ、複数のオブジェクトに対して同じハッシュを返すことができます。説明についてはコメントを参照してください。ただし、同じSystem.identityHashCode()
を持つ2つが見つかるまでオブジェクトを継続的に生成するプログラムの例を次に示します。実行すると、平均して約86,000の長いラッパーオブジェクト(およびキーの整数ラッパー)をマップに追加した後、一致する2つのSystem.identityHashCode()
sをすばやく見つけます。
public static void main(String[] args) {
Map<Integer,Long> map = new HashMap<>();
Random generator = new Random();
Collection<Integer> counts = new LinkedList<>();
Long object = generator.nextLong();
// We use the identityHashCode as the key into the map
// This makes it easier to check if any other objects
// have the same key.
int hash = System.identityHashCode(object);
while (!map.containsKey(hash)) {
map.put(hash, object);
object = generator.nextLong();
hash = System.identityHashCode(object);
}
System.out.println("Identical maps for size: " + map.size());
System.out.println("First object value: " + object);
System.out.println("Second object value: " + map.get(hash));
System.out.println("First object identityHash: " + System.identityHashCode(object));
System.out.println("Second object identityHash: " + System.identityHashCode(map.get(hash)));
}
出力例:
Identical maps for size: 105822
First object value: 7446391633043190962
Second object value: -8143651927768852586
First object identityHash: 2134400190
Second object identityHash: 2134400190
それらがどのように実装されているかを知りたい場合は、ソースを読むことをお勧めします。 IDEを使用している場合は、興味のあるメソッドで+を実行し、メソッドの実装方法を確認できます。それができない場合は、ソースをGoogleで検索できます。
たとえば、Integer.hashCode()は次のように実装されます。
public int hashCode() {
return value;
}
およびString.hashCode()
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
hashCode()
メソッドは、オブジェクトを識別するためによく使用されます。 Object
実装はオブジェクトのポインター(実際のポインターではなく、一意のIDまたはそのようなもの)を返すと思います。ただし、ほとんどのクラスはメソッドをオーバーライドします。 String
クラスと同様。 2つのStringオブジェクトには同じポインターはありませんが、同じです:
_new String("a").hashCode() == new String("a").hashCode()
_
hashCode()
の最も一般的な用途は、Hashtable
、HashSet
などです。
Edit:(最近のダウン投票のため、JVMパラメーターについて読んだ記事に基づいて)
JVMパラメーター_-XX:hashCode
_を使用すると、hashCodeの計算方法を変更できます(Java Specialists 'Newsletter)の Issue 222 を参照)。
HashCode == 0:オブジェクトのメモリ内の場所に関係なく、単に乱数を返します。私が知る限り、シードのグローバルな読み取り/書き込みは、多くのプロセッサを搭載したシステムには最適ではありません。
HashCode == 1:ハッシュコード値をカウントアップします。開始する値は不明ですが、かなり高いようです。
HashCode == 2:常にまったく同じIDハッシュコード1を返します。これを使用して、オブジェクトIDに依存するコードをテストできます。上記の例でJavaChampionTestがKirkのURLを返した理由は、すべてのオブジェクトが同じハッシュコードを返していたためです。
HashCode == 3:ゼロから開始して、ハッシュコード値をカウントアップします。スレッドセーフではないため、複数のスレッドが同じハッシュコードを持つオブジェクトを生成する可能性があります。
HashCode == 4:これは、オブジェクトが作成されたメモリの場所と何らかの関係があるようです。
HashCode> = 5:これはJava 8のデフォルトのアルゴリズムであり、スレッドごとのシードを持ちます。Marsagliaのxor-shiftスキームを使用して擬似乱数を生成します。
オブジェクトのメモリ参照であると読みました。
いいえ。Object.hashCode()
は約14年前にメモリアドレスを返していました。それ以来。
値の種類は何ですか
それが何であるかは、あなたが話しているクラスと、 `Object.hashCode()をオーバーライドしているかどうかに完全に依存します。
Object.hashCode()は、メモリが正しく機能する場合(Java.lang.ObjectのJavaDocを確認)、実装に依存し、オブジェクトに応じて変化します(Sun JVMはオブジェクトへの参照の値から値を取得します) )。
重要なオブジェクトを実装していて、それらをHashMapまたはHashSetに正しく格納する場合は、hashCode()およびequals()をオーバーライドする必要があることに注意してください。 hashCode()は何でもできます(完全に正当ですが、1を返すのは最適ではありません)が、equals()メソッドがtrueを返す場合、両方のオブジェクトのhashCode()によって返される値が等しいことが重要です。
HashCode()とequals()の混乱と理解不足は、バグの大きな原因です。 Object.hashCode()およびObject.equals()のJavaDocsを十分に理解していることを確認してください。そして、費やした時間がそれ自体に役立つことを保証します。
OpenJDKソース(JDK8)から:
デフォルトの5を使用してハッシュコードを生成します。
product(intx, hashCode, 5,
"(Unstable) select hashCode generation algorithm")
スレッドごとにシードが開始されるいくつかの定数データとランダムに生成された数値:
// thread-specific hashCode stream generator state - Marsaglia shift-xor form
_hashStateX = os::random() ;
_hashStateY = 842502087 ;
_hashStateZ = 0x8767 ; // (int)(3579807591LL & 0xffff) ;
_hashStateW = 273326509 ;
次に、この関数はhashCodeを作成します(上記で指定したデフォルトは5です)。
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
// This form uses an unguarded global Park-Miller RNG,
// so it's possible for two threads to race and generate the same RNG.
// On MP system we'll have lots of RW access to a global, so the
// mechanism induces lots of coherency traffic.
value = os::random() ;
} else
if (hashCode == 1) {
// This variation has the property of being stable (idempotent)
// between STW operations. This can be useful in some of the 1-0
// synchronization schemes.
intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = cast_from_oop<intptr_t>(obj) ;
} else {
// Marsaglia's xor-shift scheme with thread-specific state
// This is probably the best overall implementation -- we'll
// likely make this the default in future releases.
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
そのため、少なくともJDK8では、デフォルトがランダムなスレッド固有に設定されていることがわかります。
合理的に実用的である限り、Objectクラスによって定義されたhashCodeメソッドは、個別のオブジェクトに対して個別の整数を返します。 (これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法はJava™プログラミング言語では必要ありません。)
https://docs.Oracle.com/javase/8/docs/api/Java/lang/Object.html#hashCode--
public static int murmur3_32(int paramInt1, char[] paramArrayOfChar, int paramInt2, int paramInt3) {
/* 121 */ int i = paramInt1;
/* */
/* 123 */ int j = paramInt2;
/* 124 */ int k = paramInt3;
/* */
/* */ int m;
/* 127 */ while (k >= 2) {
/* 128 */ m = paramArrayOfChar[(j++)] & 0xFFFF | paramArrayOfChar[(j++)] << '\020';
/* */
/* 130 */ k -= 2;
/* */
/* 132 */ m *= -862048943;
/* 133 */ m = Integer.rotateLeft(m, 15);
/* 134 */ m *= 461845907;
/* */
/* 136 */ i ^= m;
/* 137 */ i = Integer.rotateLeft(i, 13);
/* 138 */ i = i * 5 + -430675100;
/* */ }
/* */
/* */
/* */
/* 143 */ if (k > 0) {
/* 144 */ m = paramArrayOfChar[j];
/* */
/* 146 */ m *= -862048943;
/* 147 */ m = Integer.rotateLeft(m, 15);
/* 148 */ m *= 461845907;
/* 149 */ i ^= m;
/* */ }
/* */
/* */
/* */
/* 154 */ i ^= paramInt3 * 2;
/* */
/* */
/* 157 */ i ^= i >>> 16;
/* 158 */ i *= -2048144789;
/* 159 */ i ^= i >>> 13;
/* 160 */ i *= -1028477387;
/* 161 */ i ^= i >>> 16;
/* */
/* 163 */ return i;
/* */ }
本当に学びたいのであれば、Hashing.classにあるこのコードをご覧ください。
ここで最初のパラメーターHASHING_SEEDは以下のコードに基づいて計算されます
{
long nanos = System.nanoTime();
long now = System.currentTimeMillis();
int SEED_MATERIAL[] = {
System.identityHashCode(String.class),
System.identityHashCode(System.class),
(int) (nanos >>> 32),
(int) nanos,
(int) (now >>> 32),
(int) now,
(int) (System.nanoTime() >>> 2)
};
// Use murmur3 to scramble the seeding material.
// Inline implementation to avoid loading classes
int h1 = 0;
// body
for (int k1 : SEED_MATERIAL) {
k1 *= 0xcc9e2d51;
k1 = (k1 << 15) | (k1 >>> 17);
k1 *= 0x1b873593;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1 = h1 * 5 + 0xe6546b64;
}
// tail (always empty, as body is always 32-bit chunks)
// finalization
h1 ^= SEED_MATERIAL.length * 4;
// finalization mix force all bits of a hash block to avalanche
h1 ^= h1 >>> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >>> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >>> 16;
HASHING_SEED = h1;
}
2番目のパラメーターはStringのchar配列、3番目は常に「0」、4番目のパラメーターはchar配列の長さです。
また、上記の計算は、単に文字列ハッシュコード用です。
すべての整数について、そのハッシュコードは整数値になります。 char(2文字まで)の場合、ASCII code。