Java Challengers # 4: Vergleichen von Objekten mit equals () und hashCode ()

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.


gleich-Hashcode


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) { // 1 if (this == o) { return true; } // 2 if (o == null || getClass() != o.getClass()) { return false; } // 3 Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

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 trueführe equals()
gibt falsefü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 ...
truetrue oder false
falsefalse

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 ...
truetrue
falsetrue 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

Source: https://habr.com/ru/post/de443710/


All Articles