1. Objecten vergelijken in Java

In Java kunnen objecten zowel op referentie als op waarde worden vergeleken.

Referenties vergelijken

Als twee variabelen naar hetzelfde object in het geheugen wijzen, zijn de referenties die in deze variabelen zijn opgeslagen gelijk. Als je deze variabelen vergelijkt met de gelijkheidsoperator ( ==), krijg je waar, en dat resultaat is logisch. Alles is hier eenvoudig.

Code Console-uitvoer
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Vergelijken op waarde

Maar u kunt vaak situaties tegenkomen waarin twee variabelen verwijzen naar twee afzonderlijke objecten die identiek zijn. Bijvoorbeeld twee verschillende strings-objecten die dezelfde tekst bevatten.

Gebruik de methode om te bepalen of verschillende objecten identiek zijn equals(). Bijvoorbeeld:

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


false
true

De equalsmethode is niet beperkt tot de Stringklas. Elke klas heeft het.

Zelfs lessen die je zelf schrijft, en dit is waarom.



2. Objectklasse

Alle klassen in Java erven de Objectklasse. De makers van Java bedachten deze aanpak.

En als een klasse de Objectklasse overerft, krijgt deze alle methoden van de Objectklasse. En dit is een belangrijk gevolg van overerving.

Met andere woorden, elke klasse heeft de methoden van de Objectklasse, zelfs als hun code ze niet vermeldt.

Deze geërfde methoden omvatten methoden die verband houden met objectvergelijking. Dit zijn de methoden equals()en hashCode().

Code In werkelijkheid is dit wat we zullen hebben:
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
   }
}

In het bovenstaande voorbeeld hebben we een eenvoudige Personklasse gemaakt met naam- en leeftijdsparameters, maar geen enkele methode. Maar omdat alle klassen de Objectklasse erven, Personheeft de klasse automatisch twee methoden:

Methode Beschrijving
boolean equals(Object obj)
Vergelijkt het huidige object en het doorgegeven object
int hashCode()
Retourneert de hashcode van het huidige object

Het blijkt dat absoluut elk object de equalsmethode heeft en dat objecten van verschillende typen met elkaar kunnen worden vergeleken. Dergelijke code zal perfect compileren en werken.

Code Console-uitvoer
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

De equals()methode, geërfd van de Objectklasse, implementeert het eenvoudigste algoritme voor het vergelijken van het huidige object met doorgegeven objecten: het vergelijkt alleen referenties naar de objecten.

U krijgt hetzelfde resultaat als u alleen Personvariabelen vergelijkt in plaats van de equals()methode aan te roepen. Voorbeeld:

Code Console-uitvoer
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

Wanneer de equalsmethode wordt aangeroepen a, vergelijkt deze eenvoudigweg de referentie die is opgeslagen in de avariabele met de referentie die is opgeslagen in de bvariabele.

Vergelijken werkt echter anders voor de Stringklas. Waarom?

Omdat de mensen die de Stringklasse hebben gemaakt hun eigen implementatie van de equals()methode hebben geschreven.

Implementatie van de equals()methode

Laten we nu onze eigen implementatie van de methode equals in de Personklas schrijven. We bekijken 4 hoofdgevallen.

Belangrijk:
Ongeacht welke klasse de equalsmethode overschrijft, er is altijd een Objectobject als argument nodig

Scenario 1 : hetzelfde object waarop de equalsmethode wordt aangeroepen, wordt ook doorgegeven aan de equalsmethode. Als de referenties van het huidige object en het doorgegeven object gelijk zijn, moet de methode retourneren true. Een object is gelijk aan zichzelf.

In code ziet het er zo uit:

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

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


Referenties vergelijken

Scenario 2 : nullwordt doorgegeven aan de equalsmethode — we hebben niets om mee te vergelijken. Het object waarop de equalsmethode wordt aangeroepen is beslist niet null, dus we moeten falsein dit geval terugkeren.

In code ziet het er zo uit:

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

   if (obj == null)
      return false;

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


Referenties vergelijken


Is het doorgegeven object null?

Scenario 3 : een verwijzing naar een object dat geen a is, Personwordt doorgegeven aan de equalsmethode. Is het Personobject gelijk aan het niet- Personobject? Dat is een vraag voor de ontwikkelaar van de Personklasse om te beslissen hoe hij of zij wil.

Maar meestal moeten objecten van dezelfde klasse zijn om als gelijk te worden beschouwd. Daarom, als er iets anders dan een object van de Personklasse wordt doorgegeven aan onze equals-methode, dan zullen we altijd terugkeren false. Hoe kun je het type van een object controleren? Dat klopt - door de instanceofoperator te gebruiken.

Zo ziet onze nieuwe code eruit:

Code Beschrijving
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
}


Referenties vergelijken


Is het doorgegeven object null?


Als het gepasseerde object geen aPerson

Person4. Twee objecten vergelijken

Waar zijn we mee geëindigd? Als we het einde van de methode hebben bereikt, hebben we een Personobjectreferentie die dat niet is null. Dus we converteren het naar a Personen vergelijken de relevante interne gegevens van beide objecten. En dat is ons vierde scenario .

Code Beschrijving
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
}


Referenties vergelijken


Is het doorgegeven object null?


Als het gepasseerde object geen Person


Typecasting is

En hoe vergelijk je twee Personobjecten? Ze zijn gelijk als ze dezelfde naam ( name) en leeftijd ( age) hebben. De uiteindelijke code ziet er als volgt uit:

Code Beschrijving
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;
}


Referenties vergelijken


Is het doorgegeven object null?


Als het gepasseerde object geen Person


Typecasting is

Maar dat is niet alles.

Ten eerste is het naamveld een String, dus je moet het naamveld vergelijken door de equalsmethode aan te roepen.

this.name.equals(person.name)

Ten tweede kan het nameveld zijn null: in dat geval kunt u equalser geen beroep op doen. U heeft een aanvullende controle nodig voor null:

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

Dat gezegd hebbende, als het naamveld nullin beide Personobjecten staat, zijn de namen nog steeds gelijk.

De code voor het vierde scenario kan er als volgt uitzien:

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);


Als de leeftijden niet gelijk zijn, heeft If
gelijk aan , dan heeft het geen zin om met de methode te vergelijken. Hier is ofwel het tweede veld gelijk aan , ofwel niet. Vergelijk de twee naamvelden met behulp van de methode. return false

this.namenullequalsnamenull

equals


5. hashCode()methode

Naast de equalsmethode, die bedoeld is om een ​​gedetailleerde vergelijking van alle velden van beide objecten uit te voeren, is er nog een andere methode die kan worden gebruikt voor een onnauwkeurige maar zeer snelle vergelijking: hashCode().

Stel je voor dat je een lijst van duizenden woorden alfabetisch aan het sorteren bent en dat je herhaaldelijk woordparen moet vergelijken. En de woorden zijn lang, bestaande uit veel letters. Over het algemeen duurt zo'n vergelijking erg lang.

Maar het kan versneld worden. Stel dat we woorden hebben die met verschillende letters beginnen — het is meteen duidelijk dat ze verschillend zijn. Maar als ze met dezelfde letters beginnen, dan kunnen we nog niet zeggen wat het resultaat zal zijn: de woorden kunnen gelijk of verschillend blijken te zijn.

De hashCode()methode werkt volgens een soortgelijk principe. Als je het op een object roept, geeft het een getal terug - analoog aan de eerste letter van een woord. Dit nummer heeft de volgende eigenschappen:

  • Identieke objecten hebben altijd dezelfde hashcode
  • Verschillende objecten kunnen dezelfde hashcode hebben, of hun hashcodes kunnen verschillend zijn
  • Als objecten verschillende hashcodes hebben, dan zijn de objecten beslist verschillend

Om dit nog duidelijker te maken, laten we deze eigenschappen herformuleren in termen van woorden:

  • Identieke woorden hebben altijd dezelfde beginletters.
  • Verschillende woorden kunnen dezelfde eerste letters hebben, of hun eerste letters kunnen verschillend zijn
  • Als woorden verschillende beginletters hebben, dan zijn de woorden beslist verschillend

De laatste eigenschap wordt gebruikt om de vergelijking van objecten te versnellen:

Eerst worden de hashcodes van de twee objecten berekend. Als deze hashcodes verschillend zijn, dan zijn de objecten beslist verschillend en is het niet nodig om ze verder te vergelijken.

Maar als de hashcodes hetzelfde zijn, dan moeten we de objecten nog steeds vergelijken met de equals-methode.



6. Contracten in code

Het hierboven beschreven gedrag moet door alle klassen in Java worden geïmplementeerd. Tijdens het compileren is er geen manier om te controleren of objecten correct worden vergeleken.

Java-programmeurs zijn het er algemeen over eens dat als ze hun eigen implementatie van de methode equals() schrijven en daarmee de standaardimplementatie (in de Objectklas) overschrijven, ze ook hun eigen implementatie van de hashCode()methode zo moeten schrijven dat de bovengenoemde regels tevreden.

Deze regeling wordt een contract genoemd .

Als je alleen de equals()of alleen de hashCode()methode in je klas implementeert, ben je in grove overtreding van het contract (je hebt de overeenkomst verbroken). Doe dit niet.

Als andere programmeurs uw code gebruiken, werkt deze mogelijk niet correct. Bovendien gebruikt u code die afhankelijk is van naleving van de bovenstaande contracten.

Belangrijk!

Bij het zoeken naar een element vergelijken alle Java-collecties eerst de hashcodes van objecten en voeren pas daarna een vergelijking uit met behulp van de equalsmethode.

Dat betekent dat als u uw eigen klasse een equalsmethode geeft, maar u schrijft uw eigen methode niet hashCode()of u implementeert deze verkeerd, collecties mogelijk niet correct werken met uw objecten.

U kunt bijvoorbeeld een object aan een lijst toevoegen en er vervolgens naar zoeken met behulp van de contains()methode, maar de verzameling vindt uw object mogelijk niet.