The HIRO Says

If you smell what The HIRO is cooking!!!

hashCode() メソッド(1)−基本的な実装方法

equals() メソッドの姉妹ともいえる、hashCode() メソッドについて記述します。


hashCode() メソッドの仕様

  1. 属性に変化がない場合、hashCode() を複数回呼び出しても、常に同一の値を返し続けること。
  2. a.equals(b) = true ならば、a.hashCode() = b.hashCode() であること。
  3. a.equals(b) = false ならば、a.hashCode() != b.hashCode() でなくてもよい。但し、異なる値を返したほうが親切。

あと、「Effective Java」では、「equals() をオーバーライドするならば、必ず hashCode() をオーバーライドすべし!」と記述されています。


hashCode() メソッドの役割

コレクション・フレームワークjava.util.Collection、List/Set/Map)の多くで、格納されている値同士の比較に使用されています。
HashMap#containsKey() では、key の hashCode を比較して、key があるか否かを判定しています。(JavaSE6 の実装で確認)
※「Effective Java」では、equals() で比較していると記述されていますが、実際は hashCode() でした。


なお、TreeSet や TreeMap では、hashCode ではなく Comparable#compareTo() で値を比較しています。(同じく JavaSE6 の実装で確認)


実装例

equals() メソッドの説明で使用した ComplicateObject を元に、hashCode() メソッドの実装例を記します。

import java.util.Date;

public class ComplicateObject {

// fields
    private boolean booleanValue;
    private byte    byteValue;
    private char    charValue;
    private short   shortValue;
    private int     intValue;
    private long    longValue;
    private float   floatValue;
    private double  doubleValue;
    private String  stringValue;
    private Date    dateValue;


    @Override
    public int hashCode() {

        // ●基礎となる数を用意する。(ここでは17)
        int result = 17;

        // ●特定の数値(ここでは31)を掛ける。
        // ・boolean : true なら1、false なら0。
        result = 31 * result + (this.booleanValue ? 1 : 0);

        result = 31 * result + this.byteValue;
        result = 31 * result + this.charValue;
        result = 31 * result + this.shortValue;
        result = 31 * result + this.intValue;

        // ・long : f^(f>>>32) したものを int へキャスト
        result = 31 * result + (int) (this.longValue ^ (this.longValue >>> 32));

        // ・float : Float.floatToIntBits() を呼び出す。
        result = 31 * result + Float.floatToIntBits(this.floatValue);

        // ・double : Double.doubleToLongBits() を呼び出し(結果は long)、long と同じように加工する。
        long l = Double.doubleToLongBits(this.doubleValue);
        result = 31 * result + (int) (l ^ (l >>> 32));

        // ・値が null の場合は0を、null 以外の場合はそのオブジェクトの hashCode() を計算に使う。
        result = (this.stringValue == null ? 31 * result + 0 : 31 * result + this.stringValue.hashCode());
        result = (this.dateValue   == null ? 31 * result + 0 : 31 * result + this.dateValue.hashCode());

        return result;
    }


// getter/setter/constructor は省略
}