JavaRush /Java Blog /Random-JA /コーヒーブレイク#168。Java で、equals メソッドと hashcode メソッドをオーバーライドするの...

コーヒーブレイク#168。Java で、equals メソッドと hashcode メソッドをオーバーライドするのはなぜですか?

Random-JA グループに公開済み

Java で、equals メソッドと hashcode メソッドをオーバーライドするのはなぜですか?

出典: Medium この記事では、equals()hashcode() という密接に関連した 2 つのメソッドに焦点を当てます。これらがどのように相互作用するか、そしてそれらを正しくオーバーライドする方法を学びます。 コーヒーブレイク#168。 Java で、equals メソッドと hashcode メソッドをオーバーライドするのはなぜですか?  - 1

なぜ、equals() メソッドをオーバーライドするのでしょうか?

Java では、 ==+=-+などの演算子の動作をオーバーロードできません。それらは所定のプロセスに従って動作します。たとえば、==演算子の演算を考えてみましょう。

== 演算子はどのように機能するのでしょうか?

比較されている 2 つの参照がメモリ内の同じインスタンスを指しているかどうかをチェックします。==演算子は、2 つの参照がメモリ内の同じインスタンスを表す場合にのみ true と評価されます。サンプルコードを見てみましょう。
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
プログラムで 2 つのPerson オブジェクトを異なる場所に作成し、それらを比較したいとします。
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
ビジネスの観点から見ると、この 2 つは同じに見えますよね? しかし、JVM の場合は同じではありません。どちらもnewキーワードを使用して作成されるため、これらのインスタンスは異なるメモリ セグメントに配置されます。したがって、==演算子はfalseを返します。しかし、 ==演算子をオーバーライドできない場合、これら 2 つのオブジェクトを同じように扱うように JVM に伝えるにはどうすればよいでしょうか? ここで.equals()メソッドが活躍します。equals()をオーバーライドして、一部のオブジェクトが特定のフィールドに対して同じ値を持つかどうかを確認して、それらが等しいとみなすことができます。比較するフィールドを選択できます。2 つのPersonオブジェクトが同じであるのは、年齢と名前が同じである場合に限られるとすると、IDE は次のようなもの を生成して、 equals()を自動的に作成します。
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
前の例に戻りましょう。
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
はい、==演算子をオーバーロードしてオブジェクトを希望どおりに比較することはできませんが、Java では別の方法、equals()メソッドを提供しており、これを希望どおりにオーバーライドできます。 クラスに.equals()のカスタム バージョン(オーバーライドとも呼ばれる) を提供しない場合、 Objectクラスの事前定義された.equals()==演算子は同じように動作することに注意してください。 デフォルトのquals()メソッドは、 Objectから継承され、比較される両方のインスタンスがメモリ内で同じかどうかをチェックします。

なぜ hashCode() メソッドをオーバーライドするのでしょうか?

HashSetHashMapなどの Java の一部のデータ構造は、それらの要素に適用されるハッシュ関数に基づいて要素を格納します。ハッシュ関数はhashCode()です。.equals()メソッドのオーバーライドを選択できる場合は、 hashCode()メソッドのオーバーライドも選択できるはずです。これには理由があります。結局のところ、Objectから継承されたhashCode() のデフォルトの実装では、メモリ内のすべてのオブジェクトが一意であると見なされます。これらのハッシュ データ構造に戻りましょう。これらのデータ構造には規則があります。 HashSet には重複した値を含めることはできず、HashMap には重複したキーを含めることはできません。HashSet は、各HashSet値がHashMap内のキーとして格納されるような方法でHashMap を使用して実装されます。HashMap はどのように機能しますか? HashMap は、複数のセグメントを持つネイティブ配列です。各セグメントにはリンクされたリスト ( linkedList ) があります。このリンクされたリストにはキーが保存されます。HashMap は、hashCode()メソッドを使用して各キーの正しい linkedList を見つけ、そのlinkedListのすべての要素を反復処理し、それらの各要素にquals()メソッドを適用して、その要素がそこに含まれているかどうかを確認します。重複したキーは許可されません。HashMapの中に何かを入れると、キーはこれらのリンクされたリストの 1 つに保存されます。このキーがどのリンク リストに格納されるかは、そのキーのhashCode()メソッドの結果によって示されます。つまり、key1.hashCode()の結果が 4 の場合、そのkey1 はそこに存在するLinkedList の配列の 4 番目のセグメントに格納されます。デフォルトでは、hashCode()メソッドはインスタンスごとに異なる結果を返します。==のように動作し、メモリ内のすべてのインスタンスを別のオブジェクトとして扱うデフォルトのquals()がある場合、問題はありません。覚えているかもしれませんが、前の例では、年齢と名前が同じ場合に personインスタンスが等しいとみなされるようにしたいと述べました。 コーヒーブレイク#168。 Java で、equals メソッドと hashcode メソッドをオーバーライドするのはなぜですか?  - 2
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
次に、値のペアとして特定の文字列を持つキーとしてこれらのインスタンスを保存するマップを作成しましょう。
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Personクラス では、 hashCodeメソッドはオーバーライドされていませんが、 equalsメソッドはオーバーライドされています。デフォルトのhashCode は、person1.hashCode()person2.hashCode()のJava インスタンスごとに異なる結果を与えるため、異なる結果が得られる可能性が高くなります。私たちのマップは、異なるリンクされたリスト内の異なる人物で終わる可能性があります。 これはHashMap のコーヒーブレイク#168。 Java で、equals メソッドと hashcode メソッドをオーバーライドするのはなぜですか?  - 3ロジックに反します。結局のところ、HashMap には複数の同一のキーを含めることはできません。重要なのは、 Objectクラスから継承された デフォルトのhashCode() では十分ではないということです。Personクラスのequals()メソッドをオーバーライドした後でも。そのため、equalsメソッドをオーバーライドした後にhashCode()メソッドをオーバーライドする必要があります。これを修正しましょう。hashCode()メソッドをオーバーライドして、 equals()と同じフィールド、つまりagenameを考慮するようにする必要があります。
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
hashCode()メソッド では、単純な値を使用しました (他の値を使用できます)。ただし、問題の発生を少なくするために素数を使用することをお勧めします。これらのキーをもう一度HashMapに保存してみましょう。
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode()person2.hashCode() は同じになります。それらが 0 であるとしましょう 。HashMap はセグメント 0 に移動し、その中にLinkedList がperson1 を値「1」のキーとして保存します。2 番目のケースでは、HashMap が再びバケット 0 に移動して、キーperson2 を値「2」で保存すると、それと等しい別のキーがそこにすでに存在していることがわかります。こうすると、前のキーが上書きされます。そして、 key person2だけがHashMapに存在します。 これは、複数の同一のキーを使用できないという HashMapルールの仕組みを学習した方法です。ただし、等しくないインスタンスは同じハッシュコードを持つことができ、等しいインスタンスは同じハッシュコードを返さなければならないことに注意してください。コーヒーブレイク#168。 Java で、equals メソッドと hashcode メソッドをオーバーライドするのはなぜですか?  - 4
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION