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.
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.
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.
Dies 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.
GO TO FULL VERSION