Vergleichen von Java-Objekten mit equals () und Hashcode ()

In diesem Java Challenger lernen Sie, wie equals()und hashcode()kombinieren Sie, um Objektvergleiche in Ihren Java-Programmen effizient und einfach zu gestalten. Einfach ausgedrückt, arbeiten diese Methoden zusammen, um zu überprüfen, ob zwei Objekte dieselben Werte haben.  

Ohne equals()und hashcode()müssten wir sehr große " if" Vergleiche erstellen und jedes Feld eines Objekts vergleichen. Dies würde den Code wirklich verwirrend und schwer lesbar machen. Zusammen helfen uns diese beiden Methoden, flexibleren und zusammenhängenderen Code zu erstellen.

Holen Sie sich den Java Challengers-Quellcode.

Überschreiben von equals () und Hashcode () in Java

Das Überschreiben von Methoden ist eine Technik, bei der das Verhalten der übergeordneten Klasse oder Schnittstelle erneut in die Unterklasse geschrieben (überschrieben) wird, um den Polymorphismus zu nutzen. Jedes Objectin Java enthält eine equals()und eine hashcode()Methode, die jedoch überschrieben werden müssen, um ordnungsgemäß zu funktionieren.

Um zu verstehen, wie das Überschreiben mit equals()und   funktioniert hashcode(), können wir deren Implementierung in den Java-Kernklassen untersuchen. Unten ist die equals()Methode in der ObjectKlasse. Die Methode prüft, ob die aktuelle Instanz mit der zuvor übergebenen identisch ist Object.

 public boolean equals(Object obj) { return (this == obj); } 

Wenn die hashcode()Methode nicht überschrieben wird, wird die Standardmethode in der ObjectKlasse aufgerufen. Dies ist eine native Methode , dh sie wird in einer anderen Sprache wie C ausgeführt und gibt Code bezüglich der Speicheradresse des Objekts zurück. (Es ist nicht so wichtig, genau zu wissen, wie diese Methode funktioniert, es sei denn, Sie schreiben JDK-Code.)

 @HotSpotIntrinsicCandidate public native int hashCode(); 

Wenn die Methoden equals()und hashcode()nicht überschrieben werden, werden stattdessen die oben genannten Methoden aufgerufen. In diesem Fall erfüllen die Methoden nicht den eigentlichen Zweck von equals()und hashcode(), nämlich zu prüfen, ob zwei oder mehr Objekte dieselben Werte haben.

Wenn Sie überschreiben equals(), müssen Sie in der Regel auch überschreiben hashcode().

Objekte mit equals vergleichen ()

Wir verwenden die equals()Methode, um Objekte in Java zu vergleichen. Um festzustellen, ob zwei Objekte identisch sind, werden equals()die Werte der Objektattribute verglichen:

 public class EqualsAndHashCodeExample { public static void main(String... equalsExplanation) { 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) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Simpson simpson = (Simpson) o; return age == simpson.age && weight == simpson.weight && name.equals(simpson.name); } } } 

Vergleicht im ersten Vergleich equals()die aktuelle Objektinstanz mit dem übergebenen Objekt. Wenn die beiden Objekte dieselben Werte haben, equals()wird zurückgegeben true.

equals()Überprüft im zweiten Vergleich, ob das übergebene Objekt null ist oder ob es als andere Klasse eingegeben wurde. Wenn es sich um eine andere Klasse handelt, sind die Objekte nicht gleich.

Schließlich equals()vergleicht die Objekte Felder aus . Wenn zwei Objekte dieselben Feldwerte haben, sind die Objekte identisch.

Objektvergleiche analysieren

Betrachten wir nun die Ergebnisse dieser Vergleiche in unserer main()Methode. Zunächst vergleichen wir zwei SimpsonObjekte:

 System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120))); 

Die Objekte hier sind identisch, daher wird das Ergebnis sein true.

Als nächstes vergleichen wir noch einmal zwei SimpsonObjekte:

 System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

Die Objekte hier sind fast identisch, aber ihre Namen sind unterschiedlich: Bart und El Barto. Daher wird das Ergebnis sein false.

Vergleichen wir abschließend ein SimpsonObjekt und eine Instanz der Klasse Object:

 System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

In diesem Fall liegt das Ergebnis daran, falsedass die Klassentypen unterschiedlich sind.

gleich () versus ==

Auf den ersten Blick scheinen ==Operator und equals()Methode dasselbe zu tun, aber in Wahrheit funktionieren sie unterschiedlich. Der ==Operator vergleicht, ob zwei Objektreferenzen auf dasselbe Objekt verweisen. Zum Beispiel:

 System.out.println(homer == homer2); 

Im ersten Vergleich haben wir zwei verschiedene SimpsonInstanzen mit dem newOperator instanziiert . Aus diesem Grund verweisen die Variablen homerund homer2auf unterschiedliche ObjectReferenzen im Speicherheap. Also werden wir falseals Ergebnis haben.

System.out.println(homer.equals(homer2)); 

Im zweiten Vergleich überschreiben wir die equals()Methode. In diesem Fall werden nur die Namen verglichen. Da der Name beider SimpsonObjekte "Homer" ist, wird das Ergebnis sein true.

Objekte mit Hashcode eindeutig identifizieren ()

Wir verwenden die hashcode()Methode, um die Leistung beim Vergleichen von Objekten zu optimieren. Beim Ausführen wird   hashcode()für jedes Objekt in Ihrem Programm eine eindeutige ID zurückgegeben, was das Vergleichen des gesamten Status des Objekts erheblich erleichtert.

Wenn der Hashcode eines Objekts nicht mit dem Hashcode eines anderen Objekts identisch ist, gibt es keinen Grund, die equals()Methode auszuführen : Sie wissen nur, dass die beiden Objekte nicht identisch sind. Auf der anderen Seite, wenn der Hash - Code ist das gleiche, dann müssen Sie die ausführen equals()Methode , um zu bestimmen , ob die Werte und Felder gleich sind.

Hier ist ein praktisches Beispiel mit hashcode().

 public class HashcodeConcept { public static void main(String... hashcodeExample) { Simpson homer = new Simpson(1, "Homer"); Simpson bart = new Simpson(2, "Homer"); boolean isHashcodeEquals = homer.hashCode() == bart.hashCode(); if (isHashcodeEquals) { System.out.println("Should compare with equals method too."); } else { System.out.println("Should not compare with equals method because " + "the id is different, that means the objects are not equals for sure."); } } 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  @Override public int hashCode() { return id; } } } 

A hashcode(), das immer den gleichen Wert zurückgibt, ist gültig, aber nicht sehr effektiv. In diesem Fall wird der Vergleich immer zurückgegeben true, sodass die equals()Methode immer ausgeführt wird. In diesem Fall gibt es keine Leistungsverbesserung.  

Verwenden von equals () und hashcode () mit Sammlungen

Die SetSchnittstelle ist dafür verantwortlich, dass keine doppelten Elemente in eine SetUnterklasse eingefügt werden . Im Folgenden sind einige der Klassen aufgeführt, die die SetSchnittstelle implementieren :

  • HashSet
  • TreeSet
  • LinkedHashSet
  • CopyOnWriteArraySet

In a dürfen nur eindeutige Elemente eingefügt werden. SetWenn Sie der HashSetKlasse beispielsweise ein Element hinzufügen möchten , müssen Sie zuerst die Methoden equals()und hashcode()verwenden, um zu überprüfen, ob das Element eindeutig ist. Wenn die Methoden equals()und hashcode()in diesem Fall nicht überschrieben würden, besteht die Gefahr, dass doppelte Elemente in den Code eingefügt werden.

Im folgenden Code verwenden wir die addMethode, um einem HashSetObjekt ein neues Element hinzuzufügen . HashSetÜberprüfen Sie vor dem Hinzufügen des neuen Elements, ob das Element bereits in der angegebenen Auflistung vorhanden ist:

 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; 

Wenn das Objekt dasselbe ist, wird das neue Element nicht eingefügt.

Hash-Sammlungen

Setist nicht die einzige Sammlung, die von equals()und Gebrauch macht hashcode(). HashMap, Hashtable und LinkedHashMap erfordern ebenfalls diese Methoden. Wenn Sie eine Sammlung mit dem Präfix "Hash" sehen, können Sie in der Regel sicher sein, dass die Methoden hashcode()und überschrieben equals()werden müssen, damit ihre Funktionen ordnungsgemäß funktionieren.  

Richtlinien für die Verwendung von equals () und Hashcode ()

Sie sollten eine equals()Methode nur für Objekte ausführen , die dieselbe eindeutige Hashcode-ID haben. Sie sollten nicht ausführen, equals()wenn die Hashcode-ID unterschiedlich ist.

Tabelle 1. Hashcode-Vergleiche

Wenn der hashcode()Vergleich ... Dann …
gibt true zurück ausführen equals()
gibt false zurück nicht ausführen equals()

Dieses Prinzip wird hauptsächlich in Setoder HashSammlungen aus Leistungsgründen verwendet.

Regeln für den Objektvergleich

Wenn ein hashcode()Vergleich zurückgegeben wird false, muss die equals()Methode auch false zurückgeben . Wenn der Hashcode unterschiedlich ist, sind die Objekte definitiv nicht gleich.

Tabelle 2. Objektvergleich mit Hashcode ()

Wenn der Hashcode-Vergleich zurückkehrt ... Die equals()Methode sollte zurückgeben ...
wahr richtig oder falsch
falsch falsch

Wenn die equals()Methode zurückgegeben wird true, bedeutet dies, dass die Objekte in allen Werten und Attributen gleich sind . In diesem Fall muss auch der Hashcode-Vergleich wahr sein.

Tabelle 3. Objektvergleich mit equals ()

Wenn die equals()Methode zurückkehrt ... Die hashcode()Methode sollte zurückgeben ...
wahr wahr
falsch richtig oder falsch

Nehmen Sie die Herausforderung equals () und hashcode () an!

It’s time to test your skills with the equals() and hashcode() methods.  Your goal in this challenge is to figure out the output of the two equals() method comparisons and guess the size of the Set collection.

To start, study the following code carefully:

 public class EqualsHashCodeChallenge { public static void main(String... doYourBest) { 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); } } } 

Remember, analyze the code first, guess the result, and then run the code. Your goal is to improve your skill with code analysis and absorb core Java concepts to make your code more powerful. Choose your answer before checking the correct answer below.

 A) true true 4 B) true false 3 C) true false 2 D) false true 3 

What just happened? Understanding equals() and hashcode()

In the first equals() method comparison, the result is true because the state of the object is exactly the same and the hashcode() method returns the same value for both objects.

In the second equals() method comparison, the hashcode() method is being overridden for the overridenHomer variable. The name is “Homer” for both Simpson objects, but the hashcode() method returns a different value for overriddenHomer. In this case, the final result from the the equals() method will be false because the method contains a comparison with the hashcode.

You might notice that the size of the collection is set to hold three Simpson objects. Let’s check this in a detailed way.

The first object in the set will be will be inserted normally:

 new Simpson("Homer"); 

The next object will be inserted normally, as well, because it holds a different value from the previous object:

 new Simpson("Marge"); 

Finally,  the following Simpson object has the same value as the first object. In this case the object won’t be inserted:

 set.add(new Simpson("Homer")); 

Wie wir wissen, verwendet das overridenHomerObjekt einen anderen Hashcode-Wert als die normale Simpson(“Homer”)Instanziierung. Aus diesem Grund wird dieses Element in die Sammlung eingefügt:

 overriddenHomer; 

Lösungsschlüssel

Die Antwort auf diesen Java - Herausforderer ist B . Die Ausgabe wäre:

 true false 3 

Video Herausforderung! Debugging gleich () und Hashcode ()

Das Debuggen ist eine der einfachsten Möglichkeiten, Programmierkonzepte vollständig zu erfassen und gleichzeitig Ihren Code zu verbessern. In diesem Video können Sie mitverfolgen, während ich Java debugge und erkläre equals()und hashcode()herausfordere.