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


383

最近、このDeveloper Worksドキュメントを読みました 。

このドキュメントはすべてhashCode()equals()効果的かつ正確に定義することを目的としていますが、これら2つのメソッドをオーバーライドする必要がある理由を理解できません。

これらの方法を効率的に実装するための決定をするにはどうすればよいですか?


4
これを正確に説明するprogramming.guideに2つの素晴らしい記事がありますいつ等号をオーバーライドする必要がありますか?およびequalsをオーバーライドするときに常にhashCodeをオーバーライドする必要がある理由。(警告、受け入れられた答えは実際には間違っています。)
aioobe

大文字と小文字のオーバーライドのみが等しい:2つの同じオブジェクトは異なるハッシュコードを持ちます=同じオブジェクトは異なるバケットに入ります(複製)。ハッシュコードのみをオーバーライド:2つの同じオブジェクトが同じハッシュコードを持つ=同じオブジェクトが同じバケットに入る(複製)。
VdeX

回答:


524

Joshua Blochは効果的なJavaについて語っています

equals()をオーバーライドするすべてのクラスで、hashCode()をオーバーライドする必要があります。そうしないと、Object.hashCode()の一般規約に違反し、クラスがHashMap、HashSet、Hashtableなどのすべてのハッシュベースのコレクションと連携して機能しなくなります。

オーバーライドequals()せずにオーバーライドhashCode()してを使用しようとするとどうなるかを例にして、それを理解してみましょうMap

このようなクラスがあり、2つのオブジェクトMyClassが等しい場合(それらimportantFieldがeclipse hashCode()equals()生成されている場合)

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

オーバーライドのみ equals

のみequalsがオーバーライドされている場合、myMap.put(first,someValue)最初に呼び出すと、あるバケットにハッシュされ、呼び出すmyMap.put(second,someOtherValue)と、別のバケットにハッシュされます(それらには異なるがあるためhashCode)。つまり、それらは同じですが、同じバケットにハッシュしないため、マップはそれを認識できず、両方がマップに残ります。


オーバーライドするequals()場合はオーバーライドする必要はありませんhashCode()が、2つのオブジェクトMyClassが等しい場合importantFieldは等しいが、オーバーライドしない場合は、この特定のケースで何が起こるかを見てみましょうequals()

オーバーライドのみ hashCode

あなたがこれを持っていると想像してください

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

オーバーライドするだけの場合hashCodemyMap.put(first,someValue)最初に呼び出すと、それが計算hashCodeされ、指定されたバケットに格納されます。次に、呼び出すとき、それらは等しい(ビジネス要件による)ためmyMap.put(second,someOtherValue)マップドキュメントに従って最初から2番目に置き換える必要があります。

しかし、問題は、マップのハッシュそうするとき、等号が再定義されていなかったということであるsecondオブジェクトが存在する場合にはバケツを通って、繰り返し処理が探してkいるようにsecond.equals(k)、それがどのように見つけることができません真実であるsecond.equals(first)だろうfalse

それが明確だったことを願っています


5
2つ目のケースでは、もう1つ詳しく説明してください。2つ目のオブジェクトを別のバケットに入れる必要があるのはなぜですか。
フセインアクタールワヒド「グーリ」

57
equals()をオーバーライドせずにhashCode()をオーバーライドできないことを示唆しているため、この答えは好きではありません。2つのオブジェクトを等しいと定義しているため、サンプルコード(「hashCodeのみをオーバーライドする」部分)は機能しないと言いますが、申し訳ありませんが、この定義は頭の中でしかありません。最初の例では、同じhashCodeを持つ2つの等しくないオブジェクトがあり、それは完全に合法です。したがって、equals()をオーバーライドする必要があるのは、すでにhashCode()をオーバーライドしているためではなく、「equals」定義を頭からコードに移動したいためです。
user2543253 2014

11
if you think you need to override one, then you need to override both of them間違っている。hashCodeクラスがオーバーライドするequalsが、逆は当てはまらない場合は、オーバーライドする必要があります。
akhil_mittal 2015年

4
equals()をオーバーライドせずに、hashCode()のみをオーバーライドしてもまったく問題ないと思います。これは、Effective Javaで書かれたものでもあります。books.google.fr /
ジョニー

2
@PhantomReference、だけオーバーライドノートequalsのjavadocのスペルアウト契約に違反Object「2つのオブジェクトが等しいに応じている場合はequals(Object)、その後の呼び出し、方法hashCode。同じ整数の結果を生成しなければならない2つのオブジェクトのそれぞれの方法を」もちろん、すべての契約のすべての部分がすべてのコードで実行されるわけではありませんが、正式に言えば、これは違反であり、発生するのを待っているバグであると考えます。
aioobe 2016年

264

などのコレクションHashMapとは、HashSet使用するハッシュコードがコレクション内に格納されるべきである、と方法を決定するためにオブジェクトの値をハッシュコードは、そのコレクション内のオブジェクトを見つけるために再び使用されます。

ハッシュ検索は2段階のプロセスです。

  1. 適切なバケットを見つける(を使用hashCode()
  2. バケットを検索して適切な要素を探します(を使用equals()

ここでは、書き換えてすべき理由には小さな一例ですequals()hashcode()

Employee年齢と名前の2つのフィールドを持つクラスを考えます。

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

次に、クラスを作成し、Employeeオブジェクトをに挿入してHashSet、そのオブジェクトが存在するかどうかをテストします。

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

以下を出力します:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

hashcode()メソッドのコメントを外し、同じを実行すると、出力は次のようになります:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

2つのオブジェクトが等しいと見なされた場合、それらのハッシュコードも等しい必要があるのはなぜでしょうか。それ以外の場合、クラスObjectのデフォルトのハッシュコードメソッドはequals()、2つ以上のオブジェクトが等しいと見なされるようにメソッドがオーバーライドされている場合でも、実質的に常に各オブジェクトに一意の番号を付けるため、オブジェクトを見つけることができません。 。ハッシュコードがそれを反映していなければ、オブジェクトがどれほど等しいかは関係ありません。したがって、もう一度:2つのオブジェクトが等しい場合、それらの ハッシュコードも等しくなければなりません。


4
完璧な例。明らかに違いを示しました!
coderpc

3
niceが@rajeevを説明
VdeX

2
@VikasVerma等しいオブジェクトが等しいハッシュコードを持つことは、等しくないオブジェクトが等しくないハッシュコードを持つことを意味しません。オブジェクトが実際には異なるが、それらのハッシュコードが同じである場合はどうなりますか?
ラビ

1
非常にうまく説明された:)
Rahul

4
受け入れられた答えよりもはるかに良い答え!ありがとう
ドリフトウッド2018

50

equals()をオーバーライドするすべてのクラスで、hashCode()をオーバーライドする必要があります。そうしないと、Object.hashCode()の一般規約に違反し、クラスがHashMap、HashSet、Hashtableなどのすべてのハッシュベースのコレクションと連携して機能しなくなります。


Joshua Bloch著    、Effective Javaから

定義することequals()hashCode()一貫して、あなたはハッシュベースのコレクションのキーとしてあなたのクラスのユーザビリティを向上させることができます。hashCodeのAPIドキュメントで説明されているように、「このメソッドは、によって提供されるハッシュテーブルなどの利点のためにサポートされていjava.util.Hashtableます。」

これらのメソッドを効率的に実装する方法についての質問に対する最良の答えは、Effective Javaの第3章を読むことをお勧めします。


4
これが正解です。もちろん、当然ですが、ハッシュベースのコレクションでクラスを使用しない場合は、実装していなくてもかまいませんhashCode()
スリムな

1
より複雑なケースでは、使用するコレクションがハッシュを使用しているかどうかはわからないため、「hashCode()を実装していなくても問題ありません」
Victor Sergienko

1
equals()をオーバーライドせずにhashCode()をオーバーライドできますか?
ジョニー

@StasS、はい、受け入れられた答えが言うことに反する。この記事の2番目の部分の説明を参照してください
。equalsを

22

簡単に言えば、Objectのequalsメソッドは、参照が等しいかどうかをチェックします。プロパティが等しい場合でも、クラスの2つのインスタンスが意味的に等しい場合があります。これは、たとえば、HashMapSetのように、equalsとハッシュコードを使用するコンテナーにオブジェクトを配置する場合に重要です。次のようなクラスがあるとします。

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

同じIDで 2つのインスタンスを作成します。

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

イコールをオーバーライドしないと、次のようになります。

  • 2つの異なるインスタンスであるため、a.equals(b)はfalseです。
  • 同じインスタンスであるため、a.equals(a)はtrueです。
  • 同じインスタンスであるため、b.equals(b)はtrueです

正しい?たぶん、これがあなたが望むものなら。ただし、2つの異なるインスタンスであるかどうかに関係なく、同じIDのオブジェクトを同じオブジェクトにしたいとします。イコール(およびハッシュコード)をオーバーライドします。

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

イコールとハッシュコードの実装に関しては、グアバのヘルパーメソッドを使用することをお勧めします


20

アイデンティティは平等ではありません。

  • オペレーター==テストIDに等しい。
  • equals(Object obj) メソッドは同等性テストを比較します(つまり、メソッドをオーバーライドして同等性を伝える必要があります)

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

最初に、equalsメソッドの使用を理解する必要があります。

2つのオブジェクト間の違いを特定するために、equalsメソッドをオーバーライドする必要があります。

例えば:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

hashCodeメソッドが簡単に理解できるようになりました。

hashCodeは、HashMapHashSetなどのデータ構造にオブジェクトを格納するために整数を生成します。

Customer上記のようなequalsメソッドをオーバーライドしているとします。

customer1.equals(customer2);  // returns true by our own logic

オブジェクトをバケットに保存するときのデータ構造の操作中(バケットはフォルダのファンシーな名前です)。組み込みのハッシュ手法を使用する場合、上記の2人の顧客に対して2つの異なるハッシュコードを生成します。したがって、同じオブジェクトを2つの異なる場所に格納しています。この種の問題を回避するには、次の原則に基づいてhashCodeメソッドをオーバーライドする必要があります。

  • 等しくないインスタンスは同じハッシュコードを持つ場合があります。
  • 等しいインスタンスは同じハッシュコードを返す必要があります。

3
これが最後の1時間から探していたものです。素晴らしい仲間(y)
アドナン2017

13

わかりました、簡単な言葉でコンセプトを説明しましょう。

まず、より広い観点から、コレクションがあり、ハッシュマップはコレクション内のデータ構造の1つです。

equalsとhashcodeの両方のメソッドをオーバーライドする必要がある理由を理解するために、最初にハッシュマップとは何かを理解する必要がある場合。

ハッシュマップは、データのキーと値のペアを配列形式で格納するデータ構造です。「a」の各要素がキーと値のペアであるa []としましょう。

また、上記の配列の各インデックスをリンクリストにして、1つのインデックスに複数の値を設定することもできます。

なぜハッシュマップが使われるのですか?大きな配列の中を検索する必要がある場合、それらが効率的でない場合はそれぞれを検索するので、いくつかのロジックを使用して配列を前処理し、そのロジックに基づいて要素をグループ化できるハッシュ技術は、ハッシュです。

例:配列1,2,3,4,5,6,7,8,9,10,11があり、ハッシュ関数mod 10を適用して、1,11をグループ化します。したがって、前の配列で11を検索する必要がある場合は、配列全体を反復処理する必要がありますが、グループ化すると、反復の範囲が制限されるため、速度が向上します。上記のすべての情報を格納するために使用されるデータ構造は、単純化のために2D配列と考えることができます

上記のハッシュマップとは別に、重複が追加されないこともわかります。これが、equalsとhashcodeをオーバーライドする必要がある主な理由です。

したがって、hashmapの内部動作を説明すると言ったとき、私たちはハッシュマップがどのようなメソッドを持っているか、そして私が上で説明した上記のルールに従う方法を見つける必要があります

したがって、ハッシュマップにはput(K、V)と呼ばれるメソッドがあり、ハッシュマップによれば、配列を効率的に分散し、重複を追加しないという上記のルールに従う必要があります

つまり、プットが行うことは、最初に、指定されたキーのハッシュコードを生成して、値を入れるインデックスを決定するということです。そのインデックスに何も存在しない場合は、そこに何かがすでに存在する場合、新しい値が追加されます。次に、そのインデックスでリンクリストの末尾の後に新しい値を追加する必要があります。ただし、ハッシュマップの目的の動作に従って重複を追加しないでください。2つのIntegerオブジェクトaa = 11、bb = 11があるとします。オブジェクトクラスから派生したすべてのオブジェクトと同様に、2つのオブジェクトを比較するためのデフォルトの実装では、オブジェクト内の値ではなく参照を比較します。したがって、上記の場合、意味的に等しいものの両方が等価テストに失敗し、同じハッシュコードと同じ値を持つ2つのオブジェクトが存在する可能性があるため、重複が作成されます。オーバーライドすると、重複の追加を回避できます。あなたも参照することができます詳細作業

import java.util.HashMap;


public class Employee {

String name;
String mobile;
public Employee(String name,String mobile) {
    this.name=name;
    this.mobile=mobile;
}

@Override
public int hashCode() {
    System.out.println("calling hascode method of Employee");
    String str=this.name;
    Integer sum=0;
    for(int i=0;i<str.length();i++){
        sum=sum+str.charAt(i);
    }
    return sum;

}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    System.out.println("calling equals method of Employee");
    Employee emp=(Employee)obj;
    if(this.mobile.equalsIgnoreCase(emp.mobile)){

        System.out.println("returning true");
        return true;
    }else{
        System.out.println("returning false");
        return false;
    }


}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    Employee emp=new Employee("abc", "hhh");
    Employee emp2=new Employee("abc", "hhh");
    HashMap<Employee, Employee> h=new HashMap<>();
    //for (int i=0;i<5;i++){
        h.put(emp, emp);
        h.put(emp2, emp2);

    //}

    System.out.println("----------------");
    System.out.println("size of hashmap: "+h.size());


}

}

HashMapの場合、hashCodeメソッドをオーバーライドするときに、equalsメソッドをオーバーライドする必要があるのはなぜですか。いずれの場合でも、オブジェクトのハッシュコードが等しい場合、hashmapは値を置き換えます。
Vikas Verma 2017年

@VikasVermaハッシュマップは、オブジェクトのハッシュコードが等しい場合、いかなる種類の値も置き換えません。ハッシュマップに新しく追加されたオブジェクトを配置する必要があるインデックスのみを決定します。これで、インデックスにオブジェクトが存在する可能性があるため、重複を回避するために、equalsメソッドをオーバーライドし、比較対象の2つのオブジェクトを等しいものとして扱う場合を定義するロジックを記述します。オーバーライドされない場合は、両方のオブジェクトの参照が異なるため、同じ値を持つオブジェクトが保存されます
Chetan

11

hashCode()

ハッシュコードメソッドのみをオーバーライドする場合、何も起こりません。なぜならhashCode、オブジェクトごとに常にオブジェクトクラスとしてnew を返すからです。

equals()

equalメソッドのみをオーバーライドする場合、a.equals(b)trueはhashCode、aとbのaが同じでなければならないが発生しないことを意味します。hashCodeメソッドをオーバーライドしなかったため。

注: hashCode()Objectクラスのメソッドは、常にhashCode各オブジェクトに対してnew を返します。

したがって、ハッシュベースのコレクションでオブジェクトを使用する必要がある場合は、equals()との両方をオーバーライドする必要がありますhashCode()


これは興味深い点です。hashCode()のみをオーバーライドすることです。大丈夫ですよね?それとも問題のあるケースがあるのでしょうか?
ジョニー

1
これは誤解を招く、間違った答えです。(= only =)hashCode()をオーバーライドすると、同様のプロパティを持つそれぞれのクラスのインスタンス化されているすべてのオブジェクトが同じハッシュコードを持つようになります。しかし、それらのどれもが互いに等しくないので、役に立ちません。
mfaisalhyder 2017年

8

Javaは、

「2つのオブジェクトがObjectクラスのequalsメソッドを使用して等しい場合、ハッシュコードメソッドはこれら2つのオブジェクトに同じ値を与える必要があります。」

したがって、クラスでオーバーライドequals()する場合は、hashcode()このルールに従うためにメソッドもオーバーライドする必要があります。両方の方法、equals()及びはhashcode()、で使用されるHashtableキーと値のペアのような値を格納するために、例えば、。一方をオーバーライドして他方をオーバーライドすると、そのHashtableようなオブジェクトをキーとして使用した場合に、意図したとおりに機能しない可能性があります。


6

それらをオーバーライドしない場合は、Objectのデフォルトインプリメンテーションを使用するためです。

インスタンスの等価性とhascode値は、通常、オブジェクトを構成するものの知識を必要とするため、具体的な意味を持たせるために、クラスで再定義する必要があります。


6

HashMap、Hashtableなどのコレクションで独自のクラスオブジェクトをキーとして使用するには、コレクションの内部動作を認識して、両方のメソッド(hashCode()およびequals())をオーバーライドする必要があります。そうしないと、予期しない誤った結果につながります。


6

@Lomboの回答に追加する

いつequals()をオーバーライドする必要がありますか?

Objectのequals()のデフォルトの実装は

public boolean equals(Object obj) {
        return (this == obj);
}

これは、2つのオブジェクトが同じメモリアドレスを持つ場合にのみ等しいと見なされることを意味します。これは、オブジェクトをそれ自体と比較する場合にのみ当てはまります。

ただし、1つまたは複数のプロパティの値が同じである場合、2つのオブジェクトを同じと見なすこともできます(@Lomboの回答にある例を参照してください)。

したがってequals()、これらの状況ではオーバーライドし、独自の平等条件を与えることになります。

equals()の実装に成功し、それがうまく機能しているのに、なぜhashCode()もオーバーライドするよう求めているのですか?

ユーザー定義クラスで「ハッシュ」ベースのコレクションを使用しない限り、問題ありません。しかし、将来のある時点で、あなたは使用することがありますHashMapHashSet、あなたはそうではない場合override)(ハッシュコードを「正しく実装」、これらのハッシュベースのコレクションが意図したとおりに動作しません。

等号のみオーバーライド(@Lomboの回答に追加)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

まず、HashMapは、hashCodeがsecond同じかどうかをチェックしますfirstます。値が同じである場合にのみ、同じバケット内の等価性のチェックに進みます。

ただし、ここでは、これら2つのオブジェクトのhashCodeが異なります(デフォルトの実装とは異なるメモリアドレスを持っているため)。したがって、同等性をチェックすることさえ気にしません。

オーバーライドされたequals()メソッド内にブレークポイントがある場合、それらが異なるhashCodeを持っていると、ステップインしません。 contains()チェックhashCode()し、それらが同じである場合にのみ、equals()メソッドを呼び出します。

HashMapですべてのバケットが等しいかどうかを確認できないのはなぜですか?したがって、hashCode()をオーバーライドする必要はありません。

次に、ハッシュベースのコレクションのポイントを逃しています。以下を検討してください。

Your hashCode() implementation : intObject%9.

以下は、バケットの形式で保存されたキーです。

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

たとえば、マップにキー10が含まれているかどうかを知りたいとします。すべてのバケットを検索しますか?またはバケットを1つだけ検索しますか?

hashCodeに基づいて、10が存在する場合、バケット1に存在する必要があることを識別します。したがって、バケット1のみが検索されます!!


5
class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put( 'key'、 'value')はhashCode()、バケットを決定するためにを使用してハッシュ値を計算し、equals()メソッドを使用して、値がすでにバケットに存在するかどうかを確認します。そうでない場合は追加され、それ以外の場合は現在の値に置き換えられます
  • get( 'key')はhashCode()、最初にエントリ(バケット)を見つけ、エントリequals()内の値を見つけるために使用します

両方がオーバーライドされている場合、

地図< A >

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

等しいがオーバーライドされない場合

地図< A >

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

hashCodeがオーバーライドされていない場合

地図< A >

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

HashCode Equal契約

  1. 等しいメソッドに従って等しい2つのキーは同じhashCodeを生成する必要があります
  2. 同じハッシュコードを生成する2つのキーが同じである必要はありません(上記の例では、すべての偶数が同じハッシュコードを生成します)

4

バケット内のボールをすべて黒色で収集することを検討してください。あなたの仕事は、次のようにそれらのボールを着色し、適切なゲームに使用することです、

テニス用-イエロー、レッド。クリケット用-ホワイト

現在、バケットには黄色、赤、白の3色のボールがあります。そして今あなたは着色をしましたあなただけがどの色がどのゲームにあるかを知っています。

ボールのカラーリング-ハッシング。ゲームのためのボールの選択-等しい。

あなたが着色をして、誰かがクリケットかテニスのどちらかにボールを選んだら、彼らは色を気にしません!!!


4

説明を調べていたところ、「hashCodeをオーバーライドするだけの場合myMap.put(first,someValue)、最初に呼び出すと、そのハッシュコードが計算され、指定されたバケットに保存されます。次に、myMap.put(first,someOtherValue)場合、マップドキュメントに従って、最初に2番目に置き換える必要があり(私たちの定義によると)。:

2回目に追加するときはmyMap、次のような「2番目の」オブジェクトになるはずです。myMap.put(second,someOtherValue)


4

1)よくある間違いを以下の例に示します。

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

緑の車が見つからない

2. hashCode()が原因の問題

この問題は、オーバーライドされていないメソッドが原因で発生しhashCode()ます。間の契約equals()とは、hashCode()次のとおりです。

  1. 2つのオブジェクトが等しい場合、それらは同じハッシュコードを持つ必要があります。
  2. 2つのオブジェクトが同じハッシュコードを持っている場合、それらは等しい場合と等しくない場合があります。

    public int hashCode(){  
      return this.color.hashCode(); 
    }

4

値オブジェクトを使用するときに役立ちます。以下は、Portland Pattern Repositoryからの抜粋です。

値オブジェクトの例は、数値、日付、金額、文字列などです。通常、これらは非常に広く使用されている小さなオブジェクトです。それらのアイデンティティは、オブジェクトのアイデンティティではなく状態に基づいています。このようにして、同じ概念値オブジェクトの複数のコピーを持つことができます。

したがって、1998年1月16日の日付を表すオブジェクトのコピーを複数持つことができます。これらのコピーはすべて互いに等しくなります。このような小さなオブジェクトの場合、日付を表すために単一のオブジェクトに依存するよりも、新しいオブジェクトを作成して移動する方が簡単なことがよくあります。

値オブジェクトは、常にJava(またはSmalltalkでは=)の.equals()をオーバーライドする必要があります。(.hashCode()もオーバーライドすることを忘れないでください。)


3

他の2つの(B)(C)を集約するクラス(A)があり、(A)のインスタンスをハッシュテーブル内に格納する必要があるとします。デフォルトの実装ではインスタンスの区別のみが可能で、(B)と(C)による区別はできません。したがって、Aの2つのインスタンスは等しい可能性がありますが、デフォルトでは、それらを正しい方法で比較することはできません。


3

equalsメソッドとhashcodeはオブジェクトクラスで定義されます。デフォルトでは、equalsメソッドがtrueを返す場合、システムはさらに進んでハッシュコードの値をチェックします。2つのオブジェクトのハッシュコードも同じ場合のみ、オブジェクトは同じと見なされます。したがって、equalsメソッドのみをオーバーライドする場合、オーバーライドされたequalsメソッドは2つのオブジェクトが等しいことを示していても、システム定義のハッシュコードは2つのオブジェクトが等しいことを示さない場合があります。したがって、ハッシュコードもオーバーライドする必要があります。


equalsメソッドがtrueを返す場合、ハッシュコードを確認する必要はありません。ただし、2つのオブジェクトのハッシュコードが異なる場合は、equalsを呼び出さなくても、それらを異なるオブジェクトと見なすことができます。さらに、リスト上のすべてのものが特定のハッシュコードを持たないという知識は、リスト上のすべてのものがオブジェクトとそのハッシュコードを照合できないことを意味します。簡単な例として、ハッシュコードが偶数のオブジェクトのリストと、それらが奇数のオブジェクトのリストがある場合、ハッシュコードが偶数のオブジェクトは2番目のリストにありません。
スーパーキャット2013

「等しい」メソッドが一致することを示す2つのオブジェクトXとYがあり、Xのハッシュコードが偶数で、Yのハッシュコードが奇数の場合、上記のコレクションでは、オブジェクトYのハッシュコードが奇数であり、保存されていました。 2番目のリストのオブジェクトは、オブジェクトXに一致するものを見つけることができません。Xのハッシュコードが偶数であることがわかります。2番目のリストには、偶数番号のハッシュコードを持つオブジェクトがないため、気になりません。 Xと一致して何かをそこに検索するために、YはXを一致していても、あなたが言うべき何...
supercat

...多くのコレクションは、ハッシュコードが等しくないことを意味するものの比較を回避するでしょう。ハッシュコードが不明な2つのオブジェクトが与えられた場合、ハッシュコードを計算するよりも直接比較する方が速いことがよくあります。そのため、等しくないハッシュコードを報告するものを返すtrueものequalsが一致すると見なされないという保証はありません。一方、コレクションが発生しても、同じハッシュコードを使用できないことに気づいたとしても、それらが同じであることには気付かないでしょう。
スーパーキャット2013

3

JavaのEqualsメソッドとHashcodeメソッド

これらは、すべてのクラスのスーパークラスであるjava.lang.Objectクラスのメソッドです(カスタムクラスと、Java APIで定義されたその他のクラス)。

実装:

public boolean equals(Object obj)

public int hashCode()

ここに画像の説明を入力してください

public boolean equals(Object obj)

このメソッドは、2つのオブジェクト参照xとyが同じオブジェクトを参照しているかどうかを確認するだけです。つまり、x == yかどうかをチェックします。

それは反射的です:参照値xの場合、x.equals(x)はtrueを返す必要があります。

それは対称的です:参照値xとyについて、y.equals(x)がtrueを返す場合のみ、x.equals(y)はtrueを返します。

それは推移的です:すべての参照値x、y、zについて、x.equals(y)がtrueを返し、y.equals(z)がtrueを返す場合、x.equals(z)はtrueを返します。

これは一貫しています。オブジェクトの等値比較で使用される情報が変更されていない限り、x.equals(y)の複数の呼び出しは常にtrueまたは一貫してfalseを返します。

null以外の参照値xの場合、x.equals(null)はfalseを返す必要があります。

public int hashCode()

このメソッドは、このメソッドが呼び出されたオブジェクトのハッシュコード値を返します。このメソッドは、ハッシュコード値を整数として返し、Hashtable、HashMap、HashSetなどのハッシュベースのコレクションクラスのためにサポートされています。このメソッドは、equalsメソッドをオーバーライドするすべてのクラスでオーバーライドする必要があります。

hashCodeの一般的な規約は次のとおりです。

Javaアプリケーションの実行中に同じオブジェクトで2回以上呼び出される場合、オブジェクトの等値比較で使用される情報が変更されていなければ、hashCodeメソッドは常に同じ整数を返す必要があります。

この整数は、アプリケーションのある実行から同じアプリケーションの別の実行まで一貫性を保つ必要はありません。

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

equals(java.lang.Object)メソッドに従って2つのオブジェクトが等しくない場合、2つのオブジェクトのそれぞれでhashCodeメソッドを呼び出すと、異なる整数の結果が生成される必要はありません。ただし、プログラマは、異なるオブジェクトに対して異なる整数の結果を生成すると、ハッシュテーブルのパフォーマンスが向上する可能性があることに注意する必要があります。

等しいオブジェクトは等しい限り同じハッシュコードを生成する必要がありますが、等しくないオブジェクトは異なるハッシュコードを生成する必要はありません。

リソース:

JavaRanch

画像


画像(ビデオリンク)はプライベートモードです。視聴するには公開してください。
UdayKiran Pulipati

2

以下の例では、Personクラスのequalsまたはhashcodeのオーバーライドをコメント化すると、このコードはトムの注文の検索に失敗します。ハッシュコードのデフォルトの実装を使用すると、ハッシュテーブル検索でエラーが発生する可能性があります。

以下に示すのは、Personによる人々の注文を引き上げる簡略化されたコードです。人はハッシュテーブルのキーとして使用されています。

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}

2

文字列クラスとラッパークラスの実装が異なりequals()hashCode()、Objectクラスメソッドます。Objectクラスのequals()メソッドは、内容ではなくオブジェクトの参照を比較します。ObjectクラスのhashCode()メソッドは、内容が同じであるかどうかに関係なく、すべてのオブジェクトに対して個別のハッシュコードを返します。

Mapコレクションを使用していて、キーが永続タイプであるStringBuffer / builderタイプである場合、問題が発生します。Stringクラスとは異なり、equals()とhashCode()をオーバーライドしないため、2つの異なるオブジェクトを比較すると、両方が同じ内容であっても、equals()はfalseを返します。同じコンテンツキーを格納するhashMapを作成します。同じコンテンツキーを保存すると、マップでは重複するキーがまったく許可されないため、マップのルールに違反します。したがって、クラスのequals()およびhashCode()メソッドをオーバーライドし、実装を提供して(IDEはこれらのメソッドを生成できます)、これらがStringのequals()およびhashCode()と同じように機能し、同じコンテンツキーを防止します。

equals()はハッシュコードに従って機能するため、equals()とともにhashCode()メソッドをオーバーライドする必要があります。

さらに、equals()とともにhashCode()メソッドをオーバーライドすると、equals()-hashCode()コントラクトに影響を与えません:「2つのオブジェクトが等しい場合、それらは同じハッシュコードを持つ必要があります。」

hashCode()のカスタム実装を作成する必要があるのはいつですか?

私たちが知っているように、HashMapの内部処理はハッシュの原則に基づいています。エントリセットが格納される特定のバケットがあります。同じカテゴリオブジェクトを同じインデックスに格納できるように、必要に応じてhashCode()実装をカスタマイズします。put(k,v)メソッドを使用してMapコレクションに値を格納する場合、put()の内部実装は次のとおりです。

put(k, v){
hash(k);
index=hash & (n-1);
}

つまり、インデックスを生成し、特定のキーオブジェクトのハッシュコードに基づいてインデックスが生成されます。したがって、同じハッシュコードエントリセットが同じバケットまたはインデックスに格納されるため、このメソッドで要件に応じたハッシュコードを生成します。

それでおしまい!


1

hashCode()メソッドは、特定のオブジェクトの一意の整数を取得するために使用されます。この整数は、このオブジェクトがいくつかに格納する必要がある場合、バケットの場所を決定するために使用されHashTableHashMapデータ構造などが含まれます。デフォルトでは、ObjectのhashCode()メソッドはオブジェクトが格納されているメモリアドレスの整数表現を返します。

hashCode()オブジェクトのメソッドは、オブジェクトをHashTableHashMapまたはに挿入するときに使用されますHashSetHashTablesWikipedia.orgで詳細を参照してください。

マップデータ構造にエントリを挿入するには、キーと値の両方が必要です。キーと値の両方がユーザー定義のデータ型である場合、キーのhashCode()はオブジェクトを内部的に格納する場所を決定します。マップからオブジェクトを検索する必要がある場合も、キーのハッシュコードによって、オブジェクトを検索する場所が決定されます。

ハッシュコードは、内部的に特定の「領域」(またはリスト、バケットなど)のみを指します。異なるキーオブジェクトが同じハッシュコードを持つ可能性があるため、ハッシュコード自体は正しいキーが見つかる保証はありません。次にHashTable、この領域(同じハッシュコードを持つすべてのキー)を反復処理し、キーのequals()メソッドを使用して適切なキーを見つけます。正しいキーが見つかると、そのキーに格納されているオブジェクトが返されます。

ご覧のとおり、hashCode()との組み合わせは、でequals()オブジェクトを保存したり検索したりするときに使用されますHashTable

ノート:

  1. 生成するオブジェクトhashCode()equals()その両方の属性は常に同じにします。私たちの場合と同様に、従業員IDを使用しました。

  2. equals() 一貫している必要があります(オブジェクトが変更されない場合は、同じ値を返し続ける必要があります)。

  3. いつでもa.equals(b)a.hashCode()と同じでなければなりませんb.hashCode()

  4. 一方をオーバーライドする場合は、もう一方をオーバーライドする必要があります。

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html


hashCode()すべてのオブジェクトに一意の整数を返すために使用されません。それは不可能です。これは、4番目の段落の2番目の文で矛盾しています。
ローンの侯爵2017年

@EJP、ほとんどの場合、hascode()は2つの異なるオブジェクトの一意の整数を返します。しかし、2つの異なるオブジェクトに対してhascodeが衝突する可能性があります。この概念はHashcode Collisionと呼ばれます。参照してください:tech.queryhome.com/96931/...
Paramesh Korrakuti

1

私見、それはルールによると-2つのオブジェクトが等しい場合、それらは同じハッシュを持つ必要があります。つまり、等しいオブジェクトは等しいハッシュ値を生成する必要があります。

上記のように、Objectのデフォルトのequals()は==であり、アドレスで比較を行います。hashCode()は、整数(実際のアドレスのハッシュ)でアドレスを返します。

ハッシュベースのコレクションでカスタムオブジェクトを使用する必要がある場合は、equals()とhashCode()の両方をオーバーライドする必要があります。例:従業員オブジェクトのHashSetを維持する場合、より強力なhashCodeとequalsを使用しない場合2つの異なるEmployeeオブジェクトを上書きしてしまう可能性があります。これは、年齢をhashCode()として使用するときに発生しますが、従業員IDである可能性のある一意の値を使用する必要があります。


1

重複するオブジェクトをチェックするために、カスタムのequalsとhashCodeが必要です。

ハッシュコードは常に数値を返すため、アルファベットキーではなく数値を使用してオブジェクトを取得するのが常に高速です。それはどうしますか?他のオブジェクトですでに利用可能な値を渡して新しいオブジェクトを作成したと仮定します。これで、渡された値が同じであるため、新しいオブジェクトは別のオブジェクトと同じハッシュ値を返します。同じハッシュ値が返されると、JVMは毎回同じメモリアドレスに移動し、同じハッシュ値に対して複数のオブジェクトが存在する場合は、equals()メソッドを使用して正しいオブジェクトを識別します。


1

カスタムオブジェクトをMapのキーとして保存および取得する場合は、カスタムオブジェクトのequalsおよびhashCodeを常にオーバーライドする必要があります。例えば:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

ここで、p1とp2は1つのオブジェクトと見なされ、mapサイズが等しいため、サイズは1のみになります。


1
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

テストクラス

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

オブジェクトクラスでは、equals(Object obj)を使用してアドレス比較を比較します。Testクラスで2つのオブジェクトを比較した場合、equalsメソッドはfalseを返しますが、hashcode()をオーバーライドすると、コンテンツを比較して適切な結果を得ることができます。


以下のプログラムで追加したテストクラス。
Manash Ranjan Dakua

オブジェクトクラスでは、equals(Object obj)を使用してアドレス比較を比較します。Testクラスで2つのオブジェクトを比較した場合、equalsメソッドはfalseを返しますが、hashcode()をオーバーライドすると、コンテンツを比較して適切な結果を得ることができます。
Manash Ranjan Dakua

1
この回答のすぐ下にある編集リンクを使用して、回答に追加できます。回答を2つの不完全なものとして追加しないでください
Suraj Rao

1

オーバーライドしてequals()、しない場合hashcode()、あなたや他の誰かがのようなハッシュ化されたコレクションでそのクラス型を使用しない限り、問題は見つかりませんHashSet。私の前の人々は文書化された理論を何度も明確に説明してきましたが、私は非常に単純な例を提供するためにここにいます。

equals()カスタマイズされたものを意味する必要があるクラスを考えてみましょう:-

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

次に、このメインクラスを検討します。

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

これにより、次の出力が生成されます。

    true
    -----------------------------------
    true
    -----------------------------------
    1

私は結果に満足しています。しかし、私がオーバーライドしていない場合、同じメンバーのコンテンツhashCode()Rishav持つオブジェクトは、hashCodeデフォルトの動作で生成されるように、異なるものとして一意として扱われなくなるため、悪夢が発生します。これが出力されます:-

    true
    -----------------------------------
    false
    -----------------------------------
    2

0

どちらのメソッドもObjectクラスで定義されています。そして、どちらも最も単純な実装です。したがって、これらのメソッドにいくつかの実装を追加する必要がある場合は、クラスをオーバーライドします。

Exの場合:オブジェクトのequals()メソッドは、参照の等価性のみをチェックします。したがって、その状態も比較する必要がある場合は、Stringクラスで行われるようにオーバーライドできます。


-3

Bah-「equals()をオーバーライドするすべてのクラスで、hashCode()をオーバーライドする必要があります。」

[Joshua Bloch著、Effective Javaから?]

これは間違った方法ではありませんか?hashCodeのオーバーライドは、おそらくハッシュキークラスを記述していることを意味しますが、equalsのオーバーライドは確かにそうではありません。ハッシュキーとして使用されないクラスはたくさんありますが、他の理由で論理的等価性テストの方法が必要です。「等しい」を選択した場合は、このルールを過度に適用することにより、hashCode実装を作成するように求められる場合があります。達成できることは、テストされていないコードをコードベースに追加することです。これは、将来誰かをつまずかせるのを待っている悪です。また、必要のないコードを書くことはアンチアジャイルです。それはただ間違っています(そしてideが生成したものはおそらくあなたの手作りのイコールと互換性がありません)。

確かに、キーとして使用するために記述されたオブジェクトのインターフェイスを義務付けるべきだったのでしょうか。とにかく、ObjectがデフォルトのhashCode()とequals()imhoを提供してはいけません。それはおそらく多くの壊れたハッシュコレクションを奨励しています。

しかし、とにかく、「ルール」は前から後ろに書かれていると思います。その間、同等性のテスト方法に「等しい」を使用しないようにします:-(

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.