equalsをオーバーライドするときはhashCodeもオーバーライドしなさい
Object#hashCode()の規約
- 同じオブジェクト(equalsで比較する値が変わってない)場合は、常に同じhashCodeを返す。アプリケーションを2回実行した時に1回目と2回目のhashCodeは別でもよい。
- x.equals(y)がtrueなら、x.hashCode()とy.hashCode()は同じである必要がある。
- x.equals(y)がfalseの時に、x.hashCode()とy.hashCode()が異なる必要はない。が、異なるほうがハッシュテーブルとかのパフォーマンスが上がる。
Object (Java Platform SE 6)
規約の上ではこれはありなんだな。
public class Oshiri { @Override public int hashCode(){ return 100; } }
HashMap#get()
equalsは実装してるけどhashCodeは実装してない場合、equalsでは等しいがhashCodeの値は異なることになる。
HashMapにputしたインスタンスAと、getのキーになるインスタンスBが論理的に同じであっても、hashCodeの値が異なるとgetの結果nullがかえってくる。
nullが返却される流れ
①HashMapはputされたインスタンスのhashCodeをキャッシュする。
②getが呼び出されると、引数のインスタンスのhashCodeを見る。
③キャッシュ済のhashCodeに②のhashCodeがなかったら、nullを返却する。(ここでは、putされた値と引数が論理的に等しいかどうかはチェックしない)
Object#hashCode()のレシピ
- 適当なintの値を保持する。
- equalsに影響をもつフィールドの各値をintに変換する。
- booleanは0か1に
- byte,char,shortはintにキャスト(intはそのまま)
- longは(ff>>>32) *何してんだこれ
- floatはfloatToIntBits
- doubleはdoubleToLongBitsからのlongの謎計算
- オブジェクトの参照のフィルドはそいつのhashCodeを呼ぶ。それがダメならインスタンスの各要素からまた頑張って計算。nullなら0。
- 配列は全要素に対して上記 result = 適当なintの値 + result + フィールドをintに変換したやつ ↑を全フィールドに対して計算 result = 9 + result + 身長 result = 9 + result + 体重 result = 9 + result + 年齢 みたいな
- resultを返却
- あってんのか確認、テスト
もしオブジェクトがImmutableでhashCodeの計算のコストを避けたいなら、インスタンスを作った時点でhashCodeを計算してフィールドに持ってしまえばいい 。もしくはhashCodeが初めて呼び出された時に計算して2回目以降は最初に計算した値を使う。