1. Vergleichen von Objekten in Java

In Java können Objekte sowohl nach Referenz als auch nach Wert verglichen werden.

Referenzen vergleichen

Wenn zwei Variablen auf dasselbe Objekt im Speicher verweisen, sind die in diesen Variablen gespeicherten Referenzen gleich. Wenn Sie diese Variablen mit dem Gleichheitsoperator ( ==) vergleichen, erhalten Sie „wahr“, und das Ergebnis ist sinnvoll. Hier ist alles einfach.

Code Konsolenausgabe
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Vergleich nach Wert

Es kommt jedoch häufig vor, dass sich zwei Variablen auf zwei unterschiedliche Objekte beziehen, die identisch sind. Zum Beispiel zwei verschiedene String-Objekte, die denselben Text enthalten.

Um festzustellen, ob verschiedene Objekte identisch sind, verwenden Sie die equals()Methode. Zum Beispiel:

Code Konsolenausgabe
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

Die equalsMethode ist nicht auf die Klasse beschränkt String. Jede Klasse hat es.

Sogar Kurse, die Sie selbst schreiben, und hier erfahren Sie, warum.



2. ObjectKlasse

Alle Klassen in Java erben die ObjectKlasse. Die Entwickler von Java haben diesen Ansatz entwickelt.

Und wenn eine Klasse die ObjectKlasse erbt, erhält sie alle Methoden der ObjectKlasse. Und das ist eine wesentliche Folge der Vererbung.

Mit anderen Worten: Jede Klasse verfügt über die Methoden der ObjectKlasse, auch wenn sie im Code nicht erwähnt werden.

Zu diesen geerbten Methoden gehören Methoden, die sich auf den Objektvergleich beziehen. Dies sind die equals()und- hashCode()Methoden.

Code In Wirklichkeit werden wir Folgendes haben:
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

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

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

Im obigen Beispiel haben wir eine einfache PersonKlasse mit Namens- und Altersparametern erstellt, aber keine einzige Methode. Da jedoch alle Klassen die ObjectKlasse erben, Personverfügt die Klasse automatisch über zwei Methoden:

Methode Beschreibung
boolean equals(Object obj)
Vergleicht das aktuelle Objekt und das übergebene Objekt
int hashCode()
Gibt den Hashcode des aktuellen Objekts zurück

Es stellt sich heraus, dass absolut jedes Objekt über die equalsMethode verfügt und Objekte unterschiedlichen Typs miteinander verglichen werden können. Ein solcher Code wird perfekt kompiliert und funktioniert.

Code Konsolenausgabe
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals()Methode

Die equals()von der Klasse geerbte Methode Objectimplementiert den einfachsten Algorithmus zum Vergleich des aktuellen Objekts mit übergebenen Objekten: Sie vergleicht lediglich Verweise auf die Objekte.

Das gleiche Ergebnis erhalten Sie, wenn Sie nur PersonVariablen vergleichen, anstatt die equals()Methode aufzurufen. Beispiel:

Code Konsolenausgabe
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

Wenn die equalsMethode aufgerufen wird a, vergleicht sie einfach die in der aVariablen gespeicherte Referenz mit der in der bVariablen gespeicherten Referenz.

Allerdings funktioniert der Vergleich für die StringKlasse anders. Warum?

Weil die Leute, die die StringKlasse erstellt haben, ihre eigene Implementierung der equals()Methode geschrieben haben.

Implementierung der equals()Methode

Schreiben wir nun unsere eigene Implementierung der Methode equal in der PersonKlasse. Wir betrachten 4 Hauptfälle.

Wichtig:
Unabhängig davon, welche Klasse die equalsMethode überschreibt, verwendet sie immer ein ObjectObjekt als Argument

Szenario 1 : Dasselbe Objekt, für das die Methode aufgerufen wird, wird auch an die Methode equalsübergeben . equalsWenn die Referenzen des aktuellen Objekts und des übergebenen Objekts gleich sind, muss die Methode zurückgeben true. Ein Objekt ist sich selbst gleich.

Im Code sieht es so aus:

Code Beschreibung
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


Vergleichen Sie Referenzen

Szenario 2 : nullwird an die Methode übergeben equals– wir haben nichts zum Vergleich. Das Objekt, für das die equalsMethode aufgerufen wird, ist definitiv nicht null, daher müssen wir falsein diesem Fall zurückkehren.

Im Code sieht es so aus:

Code Beschreibung
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


Referenzen vergleichen


Ist das übergebene Objekt null?

Szenario 3 : Ein Verweis auf ein Objekt, das kein Objekt ist, wird an die Methode Personübergeben . equalsIst das PersonObjekt gleich dem Nicht- PersonObjekt? Das ist eine Frage, die der Entwickler der PersonKlasse nach Belieben entscheiden kann.

Aber normalerweise müssen Objekte derselben Klasse angehören, um als gleich zu gelten. Wenn also etwas anderes als ein Objekt der PersonKlasse an unsere Methode equal übergeben wird, geben wir immer zurück false. Wie kann man den Typ eines Objekts überprüfen? Das ist richtig – mit dem instanceofOperator.

So sieht unser neuer Code aus:

Code Beschreibung
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


Referenzen vergleichen


Ist das übergebene Objekt null?


Wenn das übergebene Objekt kein a istPerson

4. Vergleich zweier PersonObjekte

Was haben wir am Ende herausgefunden? Wenn wir das Ende der Methode erreicht haben, haben wir eine PersonObjektreferenz, die nicht ist null. Also konvertieren wir es in ein Personund vergleichen die relevanten internen Daten beider Objekte. Und das ist unser viertes Szenario .

Code Beschreibung
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


Referenzen vergleichen


Ist das übergebene Objekt null?


Wenn das übergebene Objekt kein Person


Typecasting ist

Und wie vergleicht man zwei PersonObjekte? Sie sind gleich, wenn sie den gleichen Namen ( name) und das gleiche Alter ( age) haben. Der endgültige Code sieht folgendermaßen aus:

Code Beschreibung
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


Referenzen vergleichen


Ist das übergebene Objekt null?


Wenn das übergebene Objekt kein Person


Typecasting ist

Aber das ist nicht alles.

Erstens ist das Namensfeld ein String, daher müssen Sie das Namensfeld vergleichen, indem Sie die equalsMethode aufrufen.

this.name.equals(person.name)

Zweitens namekann das Feld sein null: In diesem Fall können Sie equalses nicht aufrufen. Sie benötigen eine zusätzliche Prüfung für null:

this.name != null && this.name.equals(person.name)

Wenn sich das Namensfeld jedoch nullin beiden PersonObjekten befindet, sind die Namen immer noch gleich.

Der Code für das vierte Szenario könnte so aussehen:

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


Wenn das Alter nicht gleich ist,
macht ein return false

Vergleich mit der Methode keinen Sinn . Hier ist entweder das zweite Feld gleich oder nicht. Vergleichen Sie die beiden Namensfelder mit der Methode. this.namenullequalsnamenull

equals


5. hashCode()Methode

Neben der equalsMethode, die einen detaillierten Vergleich aller Felder beider Objekte durchführen soll, gibt es eine weitere Methode, die für einen ungenauen, aber sehr schnellen Vergleich verwendet werden kann: hashCode().

Stellen Sie sich vor, Sie sortieren eine Liste mit Tausenden von Wörtern alphabetisch und müssen wiederholt Wortpaare vergleichen. Und die Wörter sind lang und bestehen aus vielen Buchstaben. Generell würde ein solcher Vergleich sehr lange dauern.

Aber es kann beschleunigt werden. Angenommen, wir haben Wörter, die mit unterschiedlichen Buchstaben beginnen – es ist sofort klar, dass sie unterschiedlich sind. Aber wenn sie mit denselben Buchstaben beginnen, können wir noch nicht sagen, wie das Ergebnis aussehen wird: Es kann sein, dass die Wörter gleich oder unterschiedlich sind.

Die hashCode()Methode funktioniert nach einem ähnlichen Prinzip. Wenn Sie es für ein Objekt aufrufen, gibt es eine Zahl zurück – analog zum ersten Buchstaben eines Wortes. Diese Nummer hat die folgenden Eigenschaften:

  • Identische Objekte haben immer den gleichen Hashcode
  • Verschiedene Objekte können denselben Hashcode haben oder ihre Hashcodes können unterschiedlich sein
  • Wenn Objekte unterschiedliche Hashcodes haben, dann sind die Objekte definitiv unterschiedlich

Um dies noch deutlicher zu machen, formulieren wir diese Eigenschaften in Worte:

  • Identische Wörter haben immer die gleichen Anfangsbuchstaben.
  • Verschiedene Wörter können die gleichen Anfangsbuchstaben haben oder ihre Anfangsbuchstaben können unterschiedlich sein
  • Wenn Wörter unterschiedliche Anfangsbuchstaben haben, dann sind die Wörter definitiv unterschiedlich

Die letzte Eigenschaft wird verwendet, um den Vergleich von Objekten zu beschleunigen:

Zunächst werden die Hashcodes der beiden Objekte berechnet. Wenn diese Hashcodes unterschiedlich sind, dann sind die Objekte definitiv unterschiedlich und es besteht keine Notwendigkeit, sie weiter zu vergleichen.

Wenn die Hashcodes jedoch gleich sind, müssen wir die Objekte immer noch mit der Methode „equals“ vergleichen.



6. Verträge im Code

Das oben beschriebene Verhalten muss von allen Klassen in Java implementiert werden. Beim Kompilieren gibt es keine Möglichkeit zu überprüfen, ob Objekte korrekt verglichen werden.

Java-Programmierer sind sich allgemein darüber einig, dass sie, wenn sie ihre eigene Implementierung der Methode equal() schreiben und dadurch die Standardimplementierung (in der ObjectKlasse) überschreiben, auch ihre eigene Implementierung der hashCode()Methode so schreiben müssen, dass die oben genannten Regeln gelten befriedigt.

Diese Vereinbarung wird als Vertrag bezeichnet .

Wenn Sie nur die equals()oder nur die hashCode()Methode in Ihrer Klasse implementieren, verstoßen Sie grob gegen den Vertrag (Sie haben die Vereinbarung gebrochen). Tun Sie das nicht.

Wenn andere Programmierer Ihren Code verwenden, funktioniert er möglicherweise nicht richtig. Darüber hinaus verwenden Sie Code, der auf der Einhaltung der oben genannten Verträge basiert.

Wichtig!

Bei der Suche nach einem Element vergleichen alle Java-Sammlungen zunächst die Hashcodes von Objekten und führen erst dann einen Vergleich mit der equalsMethode durch.

Das heißt, wenn Sie Ihrer eigenen Klasse eine equalsMethode geben, aber keine eigene hashCode()Methode schreiben oder diese falsch implementieren, funktionieren Sammlungen möglicherweise nicht richtig mit Ihren Objekten.

Sie könnten beispielsweise ein Objekt zu einer Liste hinzufügen und dann mit der contains()Methode danach suchen, aber die Sammlung findet Ihr Objekt möglicherweise nicht.