Java Challengers # 4: Vergleichen von Objekten mit equals () und hashCode ()
Im Vorgriff auf den Start eines neuen Threads zum Kurs "Java Developer" übersetzen wir weiterhin eine Reihe von Java Challengers-Artikeln, deren vorherige Teile unter den folgenden Links gelesen werden können:
Lass uns gehen!
In diesem Artikel erfahren Sie, wie die Methoden equals()
und hashCode()
zusammenhängen und wie sie beim Vergleichen von Objekten verwendet werden.

Ohne equals()
und hashCode()
zu verwenden, um den Status von zwei Objekten zu vergleichen, müssen wir viele " if
" -Vergleiche schreiben, die jedes Feld des Objekts vergleichen. Dieser Ansatz macht den Code verwirrend und schwer lesbar. Zusammen helfen diese beiden Methoden dabei, flexibleren und konsistenteren Code zu erstellen.
Der Quellcode für den Artikel ist hier .
Überschreiben von equals () und hashCode ()
Das Überschreiben von Methoden ist eine Technik, bei der das Verhalten einer übergeordneten Klasse oder Schnittstelle in einer Unterklasse überschrieben (neu definiert) wird (siehe Java Challengers # 3: Polymorphism and Inheritance , Eng. ). In Java verfügt jedes Objekt über die Methoden equals()
und hashCode()
Um ordnungsgemäß zu funktionieren, müssen sie überschrieben werden.
Um zu verstehen, wie die Neudefinition von equals()
und hashCode()
funktioniert, untersuchen wir deren Implementierung in den Java-Basisklassen. Das Folgende ist die equals()
-Methode der Object
Klasse. Die Methode prüft, ob die aktuelle Instanz mit dem übergebenen obj
Objekt obj
.
public boolean equals(Object obj) { return (this == obj); }
Schauen wir uns nun die hashCode()
-Methode in der Object
Klasse an.
@HotSpotIntrinsicCandidate public native int hashCode();
Dies ist native Methode - eine Methode, die in einer anderen Sprache wie C geschrieben ist und einen numerischen Code zurückgibt, der der Speicheradresse des Objekts zugeordnet ist. (Wenn Sie keinen JDK-Code schreiben, ist es nicht wichtig, genau zu wissen, wie diese Methode funktioniert.)
Anmerkung des Übersetzers: Der mit der Adresse verknüpfte Wert ist nicht vollständig korrekt ( dank vladimir_dolzhenko ). HotSpot JVM verwendet standardmäßig Pseudozufallszahlen. Die Beschreibung der Implementierung von hashCode () für HotSpot finden Sie hier und hier .
Wenn die Methoden equals()
und hashCode()
nicht überschrieben werden, werden stattdessen die oben beschriebenen Methoden der Object
Klasse aufgerufen. In diesem Fall erfüllen die Methoden nicht den eigentlichen Zweck von equals()
und hashCode()
, hashCode()
zu überprüfen, ob Objekte denselben Status haben.
In der Regel überschreibt das Überschreiben von equals()
auch hashCode()
.
Objekte mit equals vergleichen ()
Die Methode equals()
wird zum Vergleichen von Objekten verwendet. Um festzustellen, ob Objekte identisch sind oder nicht, vergleicht equals()
die Objektfeldwerte:
public class EqualsAndHashCodeExample { public static void main(String... args){ System.out.println(new Simpson("Homer", 35, 120) .equals(new Simpson("Homer",35,120))); System.out.println(new Simpson("Bart", 10, 120) .equals(new Simpson("El Barto", 10, 45))); System.out.println(new Simpson("Lisa", 54, 60) .equals(new Object())); } static class Simpson { private String name; private int age; private int weight; public Simpson(String name, int age, int weight) { this.name = name; this.age = age; this.weight = weight; } @Override public boolean equals(Object o) {
Schauen wir uns die Methode equals()
. Der erste Vergleich vergleicht die aktuelle Instanz this
mit dem übergebenen o
. Wenn es dasselbe Objekt ist, gibt equals()
true
.
Der zweite Vergleich prüft, ob das übergebene Objekt null
und welcher Typ es ist. Wenn das übertragene Objekt von einem anderen Typ ist, sind die Objekte nicht gleich.
Schließlich vergleicht equals()
die Felder von Objekten. Wenn zwei Objekte dieselben Feldwerte haben, sind die Objekte identisch.
Analyse von Optionen zum Vergleichen von Objekten
Schauen wir uns nun die Optionen zum Vergleichen von Objekten in der main()
-Methode an. Zunächst vergleichen wir zwei Simpson
Objekte:
System.out.println( new Simpson("Homer", 35, 120).equals( new Simpson("Homer", 35, 120)));
Die Felder dieser Objekte haben dieselben Werte, sodass das Ergebnis true
.
Vergleichen Sie dann noch einmal die beiden Simpson
Objekte:
System.out.println( new Simpson("Bart", 10, 45).equals( new Simpson("El Barto", 10, 45)));
Die Objekte hier sind ähnlich, aber die Bedeutungen der Namen sind unterschiedlich: Bart und El Barto . Daher ist das Ergebnis false
.
Vergleichen wir abschließend das Simpson
Objekt und die Instanz der Object
Klasse:
System.out.println( new Simpson("Lisa", 54, 60).equals( new Object()));
In diesem Fall ist das Ergebnis false
, da die Objekttypen unterschiedlich sind.
gleich () versus ==
Auf den ersten Blick scheinen der Operator ==
und die Methode equals()
dasselbe zu tun, aber tatsächlich funktionieren sie unterschiedlich. Der Operator ==
vergleicht, ob zwei Links auf dasselbe Objekt verweisen. Zum Beispiel:
Simpson homer = new Simpson("Homer", 35, 120); Simpson homer2 = new Simpson("Homer", 35, 120); System.out.println(homer == homer2);
Wir haben mit dem new
Operator zwei verschiedene Instanzen von Simpson
. Daher verweisen die Variablen homer
und homer2
auf verschiedene Objekte im Heap . Infolgedessen werden wir false
.
Im folgenden Beispiel verwenden wir die überschriebene Methode equals()
:
System.out.println(homer.equals(homer2));
In diesem Fall werden die Felder verglichen. Da beide Simpson
Objekte dieselben Feldwerte haben, ist das Ergebnis true
.
Identifizierung von Objekten mit hashCode ()
Die hashCode()
-Methode wird verwendet, um die Leistung beim Vergleichen von Objekten zu optimieren. Die hashCode()
-Methode gibt für jedes Objekt einen eindeutigen Bezeichner zurück, was den Vergleich der Objektzustände vereinfacht.
Wenn der Hash-Code eines Objekts nicht mit dem Hash-Code eines anderen Objekts übereinstimmt, können Sie die Methode equals()
weglassen: Sie wissen nur, dass die beiden Objekte nicht übereinstimmen. Wenn der Hash-Code jedoch identisch ist, müssen Sie die Methode equals()
ausführen, um festzustellen, ob die Feldwerte übereinstimmen.
Betrachten Sie ein praktisches Beispiel mit hashCode()
.
public class HashcodeConcept { public static void main(String... args) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println(" equals."); } else { System.out.println(" equals, .. " + " , , ."); } } static class Simpson { int id; String name; public Simpson(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Simpson simpson = (Simpson) o; return id == simpson.id && name.equals(simpson.name); } @Override public int hashCode() { return id; } } }
Die hashCode()
-Methode, die immer den gleichen Wert zurückgibt, ist gültig, aber nicht effizient. In diesem Fall gibt der Vergleich immer true
, sodass die Methode equals()
immer ausgeführt wird. In diesem Fall gibt es keine Leistungsverbesserung.
Verwenden von equals () und hashCode () mit Sammlungen
Klassen, die die Set
Schnittstelle (set) implementieren, sollten verhindern, dass doppelte Elemente hinzugefügt werden. Im Folgenden sind einige Klassen aufgeführt, die die Set
Schnittstelle implementieren:
Dem Set
können nur eindeutige Elemente hinzugefügt werden. Wenn Sie beispielsweise einem HashSet
ein Element hinzufügen HashSet
, müssen Sie zuerst die Methoden equals()
und hashCode()
verwenden, um sicherzustellen, dass dieses Element eindeutig ist. Wenn die Methoden equals()
und hashCode()
nicht überschrieben wurden, besteht die Gefahr, dass doppelte Werte eingefügt werden.
Schauen wir uns den Implementierungsteil der add()
-Methode in einem HashSet
:
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e;
Vor dem Hinzufügen eines neuen Elements überprüft das HashSet
, ob das Element in dieser Sammlung vorhanden ist. Wenn das Objekt übereinstimmt, wird das neue Element nicht eingefügt.
Die Methoden equals()
und hashCode()
werden nicht nur in Set
. Diese Methoden sind auch für HashMap , Hashtable und LinkedHashMap erforderlich . Wenn Sie eine Sammlung mit dem Präfix "Hash" sehen , können Sie in der Regel sicher sein, dass für den korrekten Betrieb eine Überschreibung der Methoden hashCode()
und equals()
erforderlich ist.
Empfehlungen zur Verwendung von equals () und hashCode ()
Führen Sie die Methode equals()
nur für Objekte mit demselben Hashcode aus. Führen Sie equals()
nicht aus, wenn der Hash-Code unterschiedlich ist.
Tabelle 1. Hash-Code-Vergleich
Wenn hashCode () Vergleich ... | Das ... |
---|
gibt true | führe equals() |
gibt false | führe nicht equals() |
Dieses Prinzip wird aus Leistungsgründen hauptsächlich in Set
oder Hash
Sammlungen verwendet.
Objektvergleichsregeln
Wenn der Vergleich hashCode()
false
zurückgibt, sollte die Methode equals()
auch false
. Wenn der Hash-Code unterschiedlich ist, sind die Objekte definitiv nicht gleich.
Tabelle 2.Vergleich von Objekten mit hashCode ()
Wenn der Vergleich von hashCode() zurückgibt ... | Die Methode equals() sollte ... |
---|
true | true oder false |
false | false |
Wenn die Methode equals()
true
zurückgibt, bedeutet dies, dass die Objekte in allen Werten und Attributen gleich sind . In diesem Fall sollte auch der Hash-Code-Vergleich wahr sein.
Tabelle 3. Vergleich von Objekten mit equals ()
Wenn die equals() -Methode zurückgibt ... | Die hashCode() -Methode sollte ... |
---|
true | true |
false | true oder false |
Lösen Sie das Problem mit equals () und hashCode ()
Es ist Zeit, Ihr Wissen über die Methoden equals()
und hashCode()
zu testen. Die Aufgabe besteht darin, das Ergebnis mehrerer equals()
und die Gesamtgröße der Set
Auflistung herauszufinden.
Lesen Sie zunächst den folgenden Code sorgfältig durch:
public class EqualsHashCodeChallenge { public static void main(String... args) { System.out.println(new Simpson("Bart").equals(new Simpson("Bart"))); Simpson overriddenHomer = new Simpson("Homer") { public int hashCode() { return (43 + 777) + 1; } }; System.out.println(new Simpson("Homer").equals(overriddenHomer)); Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge"))); set.add(new Simpson("Homer")); set.add(overriddenHomer); System.out.println(set.size()); } static class Simpson { String name; Simpson(String name) { this.name = name; } @Override public boolean equals(Object obj) { Simpson otherSimpson = (Simpson) obj; return this.name.equals(otherSimpson.name) && this.hashCode() == otherSimpson.hashCode(); } @Override public int hashCode() { return (43 + 777); } } }
Analysieren Sie zunächst den Code und überlegen Sie, wie das Ergebnis aussehen wird. Und erst dann den Code ausführen. Ziel ist es, Ihre Fähigkeiten zur Codeanalyse zu verbessern und grundlegende Java-Konzepte zu erlernen, damit Sie Ihren Code verbessern können.
Was wird das Ergebnis sein?
A) true true 4 B) true false 3 C) true false 2 D) false true 3
Was ist passiert? Equals () und hashCode () verstehen
Im ersten Vergleich ist das Ergebnis von equals()
true
, da die Zustände der Objekte gleich sind und die Methode hashCode()
für beide Objekte den gleichen Wert zurückgibt.
Im zweiten Vergleich wurde die Methode hashCode()
für die Variable hashCode()
. Für beide Simpson
Objekte lautet der Name "Homer" , für overriddenHomer
die hashCode()
-Methode einen anderen Wert zurück. In diesem Fall ist das Ergebnis der Methode equals()
false
, da es einen Vergleich mit dem Hashcode enthält.
Sie müssen erkannt haben, dass die Sammlung drei Simpson
Objekte enthält. Lass es uns ausmachen.
Das erste Objekt im Set wird wie gewohnt eingefügt:
new Simpson("Homer");
Das folgende Objekt wird ebenfalls auf die übliche Weise eingefügt, da es einen anderen Wert als das vorherige Objekt enthält:
new Simpson("Marge");
Schließlich hat das nächste Simpson
Objekt denselben Namenswert wie das erste Objekt. In diesem Fall wird das Objekt nicht eingefügt:
set.add(new Simpson("Homer"));
Wie wir wissen, verwendet das overridenHomer
Objekt im Gegensatz zu einer regulären Simpson("Homer")
Instanz Simpson("Homer")
einen anderen Hash-Wert. Aus diesem Grund wird dieser Artikel in die Sammlung eingefügt:
set.add(overriddenHomer);
Die Antwort
Die richtige Antwort lautet B. Die Schlussfolgerung lautet:
true false 3
Häufige Fehler mit equals () und hashCode ()
- Fehlender überschreibender
hashCode()
zusammen mit überschreibendem equals()
oder umgekehrt. - Fehlende Überschreibungen von
equals()
und hashCode()
bei Verwendung von Hash-Sammlungen wie HashSet
. - Rückgabe eines konstanten Werts in der
hashCode()
-Methode anstelle eines eindeutigen Codes für jedes Objekt. - Äquivalente Verwendung von
==
und equals()
. Der Operator ==
vergleicht Objektreferenzen, während die Methode equals()
Objektwerte vergleicht.
Was Sie über equals () und hashCode () beachten müssen
- Es wird empfohlen, die Methoden
equals()
und hashCode()
in Ihren POJOs ( Russisch , Englisch ) immer zu überschreiben. - Verwenden Sie einen effizienten Algorithmus, um einen eindeutigen Hash-Code zu erstellen.
- Wenn Sie die Methode
equals()
überschreiben, überschreiben Sie immer die Methode hashCode()
. - Die Methode
equals()
sollte den vollständigen Status von Objekten (Werte aus Feldern) vergleichen. - Die
hashCode()
-Methode kann eine POJO-ID (ID) sein. - Wenn das Ergebnis des Vergleichs des Hash-Codes der beiden Objekte
false
, muss auch die Methode equals()
false
. - Wenn
equals()
und hashCode()
bei Verwendung von Hash-Sammlungen nicht neu definiert werden, enthält die Sammlung doppelte Elemente.
Erfahren Sie mehr über Java
Traditionell warte ich auf Ihre Kommentare und lade Sie zu einer offenen Lektion ein , die am 18. März von unserem Lehrer Sergei Petrelevich abgehalten wird