web-dev-qa-db-ja.com

equals()メソッドをオーバーライドするときにhashCode()をオーバーライドする必要があるのはなぜですか?

わかりました。多くの場所やソースから、equals()メソッドをオーバーライドするたびに、hashCode()メソッドもオーバーライドする必要があると聞いています。しかし、次のコードを検討してください

package test;

public class MyCustomObject {

    int intVal1;
    int intVal2;

    public MyCustomObject(int val1, int val2){
        intVal1 = val1;
        intVal2 = val2;
    }

    public boolean equals(Object obj){
        return (((MyCustomObject)obj).intVal1 == this.intVal1) && 
                (((MyCustomObject)obj).intVal2 == this.intVal2);
    }

    public static void main(String a[]){
        MyCustomObject m1 = new MyCustomObject(3,5);
        MyCustomObject m2 = new MyCustomObject(3,5);
        MyCustomObject m3 = new MyCustomObject(4,5);

        System.out.println(m1.equals(m2));
        System.out.println(m1.equals(m3));
    }
}

ここでの出力はtrue、falseであり、私が望むとおりに出力され、hashCode()メソッドをオーバーライドする必要はまったくありません。これは、hashCode()のオーバーライドは、誰もが言うように必須のオプションではなく、オプションであることを意味します。

2回目の確認が必要です。

31
bragboy

コードはhashCode() AP​​Iを必要とする機能(HashMap、HashTable)を使用しないため、これは機能します。

ただし、クラス(おそらく1回限りとして記述されていない)が、実際にそのオブジェクトをハッシュキーとして使用するコードで後で呼び出されるかどうかはわかりません。その場合、状況が影響を受けます。

オブジェクトクラスのドキュメント

HashCodeの一般的なコントラクトは次のとおりです。

  • Javaアプリケーションの実行中に同じオブジェクトで複数回呼び出される場合は常に、オブジェクトの同等の比較で使用される情報が変更されていない限り、hashCodeメソッドは常に同じ整数を返す必要があります。この整数は、アプリケーションの1つの実行から同じアプリケーションの別の実行まで一貫している必要はありません。

  • equals(Object)メソッドに従って2つのオブジェクトが等しい場合、2つのオブジェクトのそれぞれでhashCodeメソッドを呼び出すと、同じ整数の結果が生成される必要があります。

32
DVK

HashMap/Hashtableは最初にhashCode()によってオブジェクトを検索するためです。

それらが同じでない場合、ハッシュマップはオブジェクトが同じではないと主張し、マップに存在しないことを返します。

11
Dennis C

どちらも、または両方を_@Override_する必要がある理由は、それらがAPIの他の部分と相互に関連する方法のためです。

_m1_を_HashSet<MyCustomObject>_に入れても、contains(m2)ではないことがわかります。これは一貫性のない動作であり、多くのバグや混乱を引き起こす可能性があります。

Javaライブラリにはたくさんの機能があります。それらを機能させるにはforあなたはルールに従ってプレイし、equalshashCodeの一貫性は、最も重要なものの1つです。

5

他のコメントのほとんどはすでにあなたに答えを与えました:オブジェクトインスタンスに「インデックスを付ける」ための最適化としてhashCodeを使用するコレクション(すなわち:HashSet、HashMap)があるので、あなたはそれをする必要があります、それらの最適化は次の場合を期待します:a.equals(b) ==> a.hashCode() == b.hashCode()(逆は成り立たないことに注意してください)。

ただし、追加情報として、この演習を行うことができます。

class Box {
     private String value;
     /* some boring setters and getters for value */
     public int hashCode() { return value.hashCode(); }
     public boolean equals(Object obj) { 
           if (obj != null && getClass().equals(obj.getClass()) { 
               return ((Box) obj).value.equals(value); 
            } else { return false; }
     }
}

これを行う:

Set<Box> s = new HashSet<Box>();
Box b = new Box();
b.setValue("hello");
s.add(b);
s.contains(b); // TRUE
b.setValue("other");
s.contains(b); // FALSE
s.iterator().next() == b // TRUE!!! b is in s but contains(b) returns false

この例から学んだことは、変更可能な(変更可能な)プロパティを使用してequalsまたはhashCodeを実装することは本当に悪い考えであるということです。

4
Diego

コレクション内のhashCode()値(HashMap、HashSetなど)を使用してオブジェクトを検索する場合、これは主に重要です。各オブジェクトは異なるhashCode()値を返すため、このメソッドをオーバーライドして、オブジェクトの状態に基づいて一貫してhashCode値を生成し、Collectionsアルゴリズムがハッシュテーブル上の値を見つけられるようにする必要があります。

0
Almar