オブジェクトクラスの同値を定義するequalsメソッドを実装した場合、
hashCodeメソッドも合わせて実装しなくてはならない。
- hashCodeメソッド
- オブジェクトのハッシュ値を返すメソッド。
- ハッシュ値
- オブジェクトの値を一定のルールにしたがって数値化したもの。
同じ値を持つオブジェクトは同じハッシュ値となる。
なぜ、hashCodeメソッドが必要なのか
java.utilパッケージのHashMapやHashSetなどハッシュ系のコレクションクラスでは、
equalsメソッドによる等価判定は比較的大きな計算コストがかかるため最初にハッシュ値でオブジェクトを比較し、ハッシュ値が等しい場合に限り、equalsメソッドで判定を行う。
ハッシュ値が同じでもまれに同じ値を持っていない場合もあるのでequalsメソッドで厳密にチェックしている。
ObjectクラスのhashCodeメソッドはオブジェクトが異なれば、異なるハッシュ値を返すというデフォルトの仕様になっている。
よって、仕様通りの動きを実現するためにはhashCodeメソッドをオーバーライドしてハッシュ値を正しく返さなければならない。
HashSetクラスを例にする
Setにオブジェクトを要素として追加する場合、
二つのオブジェクトが同値であってもハッシュ値が異なるために異なるオブジェクトと認識され、
同じ値を持ったオブジェクトが重複してHashSetに追加されてしまう。
本来の仕様の動きに反している。(本来期待すべき結果は1)
import java.util.HashSet; public class User { public static void main(String[] args) { HashSet<Person> persons = new HashSet<>(); Person personA = new Person(20,"Tom"); Person personB = new Person(20,"Tom"); //同値であるオブジェクトをHashSetに追加する persons.add(personA); persons.add(personB); //HashSetに存在する要素数を取得する System.out.println(persons.size()); } }
2
equalsメソッドと同じようにhashCodeメソッドをオブジェクトクラス内でオーバーライドして、
同値である場合、同じハッシュ値を持つように定義する。
Eclipseの自動生成機能でequalsメソッドとhashCodeメソッドを同時に定義する
public class Person { private int age; private String name; public Person(int age, String name) { this.age = age; this.name = name; } //hashCodeメソッドをオーバーライド @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
再びHashSetに存在する要素数を取得しコンソールに出力する。
1
hashCodeが返す値(ハッシュ値)が等しくなり値も同じなので、
HashSet本来の仕様通り、要素が正しく上書きされて追加されるようになる。
(重複しない)
Objects.hashメソッドを使ってhashCodeを生成する事もできる。
(※Java 7からの仕様)
//hashCodeメソッドをオーバーライド @Override public int hashCode() { return Objects.hash(this.age,this.name); }
HashMap、HashSetなどが仕様通りに正しく動かせるよう、
オブジェクトクラスのequalsメソッドを定義した際にはhashCodeメソッドも忘れずに定義するようにしておきたい。