JavaRush /Java-Blog /Random-DE /Kaffeepause Nr. 168. Warum sollten Equals- und Hashcode-M...

Kaffeepause Nr. 168. Warum sollten Equals- und Hashcode-Methoden in Java überschrieben werden?

Veröffentlicht in der Gruppe Random-DE

Warum sollten Equals- und Hashcode-Methoden in Java überschrieben werden?

Quelle: Medium Dieser Artikel konzentriert sich auf zwei eng verwandte Methoden: equal() und hashcode() . Sie erfahren, wie sie miteinander interagieren und wie Sie sie richtig überschreiben. Kaffeepause Nr. 168.  Warum sollten Equals- und Hashcode-Methoden in Java überschrieben werden?  - 1

Warum überschreiben wir die Methode equal()?

In Java können wir das Verhalten von Operatoren wie == , += , -+ nicht überladen . Sie arbeiten nach einem vorgegebenen Prozess. Betrachten Sie beispielsweise die Operation des ==- Operators .

Wie funktioniert der ==-Operator?

Es prüft, ob die beiden verglichenen Referenzen auf dieselbe Instanz im Speicher verweisen. Der ==- Operator wird nur dann als wahr ausgewertet, wenn die beiden Referenzen dieselbe Instanz im Speicher darstellen. Werfen wir einen Blick auf den Beispielcode:
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
Nehmen wir an , Sie haben in Ihrem Programm zwei Personenobjekte an verschiedenen Orten erstellt und möchten diese vergleichen.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
Aus geschäftlicher Sicht sehen die beiden gleich aus, oder? Aber für die JVM sind sie nicht dasselbe. Da beide mit dem neuen Schlüsselwort erstellt werden , befinden sich diese Instanzen in unterschiedlichen Speichersegmenten. Daher gibt der ==- Operator false zurück . Aber wenn wir den ==- Operator nicht überschreiben können , wie können wir der JVM dann mitteilen, dass diese beiden Objekte gleich behandelt werden sollen? Hier kommt die Methode .equals() ins Spiel . Sie können equal() überschreiben , um zu prüfen, ob einige Objekte für bestimmte Felder die gleichen Werte haben, um sie als gleich zu betrachten. Sie können auswählen, welche Felder verglichen werden sollen. Wenn wir sagen, dass zwei Person- Objekte nur dann gleich sind, wenn sie dasselbe Alter und denselben Namen haben, dann generiert die IDE so etwas , um automatisch equal() zu erstellen:
@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);
    }
Kehren wir zu unserem vorherigen Beispiel zurück.
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!
Ja, wir können den Operator == nicht überladen, um Objekte auf die von uns gewünschte Weise zu vergleichen, aber Java bietet uns eine andere Möglichkeit – die Methode equal() , die wir nach Belieben überschreiben können. Bedenken Sie, dass sich das vordefinierte .equals() aus der Object- Klasse und der ==- Operator gleich verhalten , wenn wir in unserer Klasse nicht unsere benutzerdefinierte Version von .equals() (auch als Override bezeichnet) bereitstellen. Die von Object geerbte Standardmethode equal() prüft, ob beide verglichenen Instanzen im Speicher gleich sind!

Warum überschreiben wir die Methode hashCode()?

Einige Datenstrukturen in Java, wie HashSet und HashMap , speichern ihre Elemente basierend auf einer Hash-Funktion, die auf diese Elemente angewendet wird. Die Hash-Funktion ist hashCode() . Wenn wir die Möglichkeit haben, die Methode .equals() zu überschreiben , sollten wir auch die Möglichkeit haben, die Methode hashCode() zu überschreiben . Dafür gibt es einen Grund. Schließlich betrachtet die von Object geerbte Standardimplementierung von hashCode() alle Objekte im Speicher als eindeutig! Aber kommen wir zurück zu diesen Hash-Datenstrukturen. Für diese Datenstrukturen gibt es eine Regel. Ein HashSet kann keine doppelten Werte enthalten und eine HashMap kann keine doppelten Schlüssel enthalten. Ein HashSet wird mithilfe einer HashMap so implementiert, dass jeder HashSet- Wert als Schlüssel in der HashMap gespeichert wird . Wie funktioniert HashMap ? HashMap ist ein natives Array mit mehreren Segmenten. Jedes Segment verfügt über eine verknüpfte Liste ( linkedList ). Diese verknüpfte Liste speichert unsere Schlüssel. HashMap findet mithilfe der Methode hashCode() die richtige verknüpfte Liste für jeden Schlüssel , durchläuft dann alle Elemente dieser verknüpften Liste und wendet die Methode equal() auf jedes dieser Elemente an, um zu überprüfen, ob dieses Element dort enthalten ist. Doppelte Schlüssel sind nicht zulässig. Wenn wir etwas in eine HashMap einfügen , wird der Schlüssel in einer dieser verknüpften Listen gespeichert. In welcher verknüpften Liste dieser Schlüssel gespeichert wird, zeigt das Ergebnis der Methode hashCode() für diesen Schlüssel. Das heißt, wenn key1.hashCode() den Wert 4 ergibt, wird dieser Schlüssel1 im 4. Segment des Arrays in der dort vorhandenen LinkedList gespeichert . Standardmäßig gibt die Methode hashCode() für jede Instanz unterschiedliche Ergebnisse zurück. Wenn wir ein Standard- equals() haben , das sich wie == verhält und alle Instanzen im Speicher als unterschiedliche Objekte behandelt, dann wird es kein Problem geben. Wie Sie sich vielleicht erinnern, haben wir in unserem vorherigen Beispiel gesagt, dass wir möchten, dass Personeninstanzen als gleich angesehen werden, wenn ihr Alter und ihre Namen gleich sind. Kaffeepause Nr. 168.  Warum sollten Equals- und Hashcode-Methoden in Java überschrieben werden?  - 2
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
Jetzt erstellen wir eine Karte, um diese Instanzen als Schlüssel mit einer bestimmten Zeichenfolge als Wertepaar zu speichern.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
In der Person- Klasse haben wir die Methode hashCode nicht überschrieben , aber wir haben eine überschriebene Methode equal . Da der Standard- HashCode für verschiedene Java-Instanzen von person1.hashCode() und person2.hashCode() unterschiedliche Ergebnisse liefert , besteht eine hohe Wahrscheinlichkeit, dass unterschiedliche Ergebnisse erzielt werden. Unsere Karte kann mit verschiedenen Personen in verschiedenen verknüpften Listen enden. Kaffeepause Nr. 168.  Warum sollten Equals- und Hashcode-Methoden in Java überschrieben werden?  - 3Dies widerspricht der Logik von HashMap . Schließlich kann eine HashMap nicht mehrere identische Schlüssel haben! Der Punkt ist, dass der von der Object- Klasse geerbte Standard -hashCode() nicht ausreicht. Auch nachdem wir die Methode equal() der Klasse Person überschrieben haben . Aus diesem Grund müssen wir die Methode hashCode() überschreiben , nachdem wir die Methode equal überschrieben haben . Lassen Sie uns das jetzt beheben. Wir müssen unsere Methode hashCode() überschreiben, damit sie dieselben Felder wie equal() berücksichtigt , nämlich age und name .
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);
    }
In der Methode hashCode() haben wir einen einfachen Wert verwendet (Sie können beliebige andere Werte verwenden). Es wird jedoch empfohlen, Primzahlen zu verwenden, um weniger Probleme zu verursachen. Versuchen wir noch einmal , diese Schlüssel in unserer HashMap zu speichern :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() und person2.hashCode() werden gleich sein. Nehmen wir an, sie sind 0. Die HashMap geht zum Segment 0 und darin speichert die LinkedList person1 als Schlüssel mit dem Wert „1“. Im zweiten Fall, wenn HashMap erneut zu Bucket 0 wechselt, um den Schlüssel person2 mit dem Wert „2“ zu speichern, wird festgestellt, dass dort bereits ein anderer gleichwertiger Schlüssel vorhanden ist. Auf diese Weise wird der vorherige Schlüssel überschrieben. Und nur die Schlüsselperson2 wird in unserer HashMap existieren . So haben wir erfahren, wie die HashMap- Regel funktioniert , die besagt, dass Sie nicht mehrere identische Schlüssel verwenden können! Beachten Sie jedoch, dass ungleiche Instanzen denselben Hashcode haben können und gleiche Instanzen denselben Hashcode zurückgeben müssen.Kaffeepause Nr. 168.  Warum sollten Equals- und Hashcode-Methoden in Java überschrieben werden?  - 4
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION