Points & Lines

【Java】hashCodeメソッドについて

オブジェクトクラスの同値を定義するequalsメソッドを実装した場合、
hashCodeメソッドも合わせて実装しなくてはならない。

equalsメソッドについての記事はこちら

hashCodeメソッド
オブジェクトのハッシュ値を返すメソッド。
ハッシュ値
オブジェクトの値を一定のルールにしたがって数値化したもの。
ハッシュ値に期待すべき事
インスタンスが異なればハッシュ値も異なる。
同じ値を持つオブジェクトは同じハッシュ値となる。

 

なぜ、hashCodeメソッドが必要なのか

java.utilパッケージのHashMapやHashSetなどハッシュ系のコレクションクラスでは、
equalsメソッドによる等価判定は比較的大きな計算コストがかかるため最初にハッシュ値でオブジェクトを比較し、ハッシュ値が等しい場合に限り、equalsメソッドで判定を行う。

ハッシュ値が同じでもまれに同じ値を持っていない場合もあるのでequalsメソッドで厳密にチェックしている。

ObjectクラスのhashCodeメソッドはオブジェクトが異なれば、異なるハッシュ値を返すというデフォルトの仕様になっている。

よって、仕様通りの動きを実現するためにはhashCodeメソッドをオーバーライドしてハッシュ値を正しく返さなければならない。

HashSetクラスを例にする

Setにオブジェクトを要素として追加する場合、
二つのオブジェクトが同値であってもハッシュ値が異なるために異なるオブジェクトと認識され、
同じ値を持ったオブジェクトが重複してHashSetに追加されてしまう。

HashSet(Setインターフェース)は重複する要素を保持しない特徴を持っているため、
本来の仕様の動きに反している。(本来期待すべき結果は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メソッドも忘れずに定義するようにしておきたい。

Follow me!

モバイルバージョンを終了