Object.equals()とObject.hashCode()のデフォルト実装
java.lang.Objectのequals()とhashCode()の実装について調べました。
Object.equals()
JDK 8 での実装は下記の通り、参照値を比較しているだけです。
public boolean equals(Object obj) { return (this == obj); }
jdk8u/jdk8u/jdk: a71d26266469 src/share/classes/java/lang/Object.java
参照値とはすなわちオブジェクトへのポインタなので、Object.equals()は同一のインスタンスであるときだけtrueを返します。
The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.
参照値(単に参照とも)はオブジェクトへのポインタまたはnullです。nullはオブジェクトを参照しない特殊な参照値です。
Object.hashCode()
hashCode()はJVMの実装に依存します。
こちらの記事でとても詳しく説明されていました。 結局のところ
- OpenJDK 8, OpenJDK 9
- スレッドごとに持つ値をXorshiftした数
- OpenJDK 7, OpenJDK 6
- 乱数
とのことでした。 XorShiftは、排他的論理和とビットシフトのみで(高速に)疑似乱数を生成できるアルゴリズムです。 hash codeは一度生成されるとObject Headerと呼ばれるインスタンスごとに持つ領域に記録されます。 二回目以降のhashCode()の呼び出し時には、Object Headerに記録されたhash codeを返します。
OpenJDK8でhash codeを生成するコードは次の通りです。
// 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 ;
jdk8u/jdk8u/hotspot: 87ee5ee27509 src/share/vm/runtime/synchronizer.cpp
スレッドのメンバ変数_hashStateX
と_hashStateW
を使ってハッシュコードを生成します。
xor-shiftのアルゴリズムは理解できていないのですが、hashStateX, hashStateY, hashStateWをアルファベット順で一つ前の名前を持つ変数に値を移し、hashStateWを生成したハッシュコードにすることで、次の呼び出し時にもユニークな値が生成できるようにしています。
_hashStateはスレッド生成時に次のように初期化されます。
_hashStateX = os::random() ; _hashStateY = 842502087 ; _hashStateZ = 0x8767 ; // (int)(3579807591LL & 0xffff) ; _hashStateW = 273326509 ;
jdk8u/jdk8u/hotspot: 87ee5ee27509 src/share/vm/runtime/thread.cpp