1. Java objektumok összehasonlítása

Java-ban az objektumok referencia és érték alapján is összehasonlíthatók.

Referenciák összehasonlítása

Ha két változó ugyanarra az objektumra mutat a memóriában, akkor az ezekben a változókban tárolt hivatkozások egyenlőek. Ha összehasonlítja ezeket a változókat az egyenlőség operátorral ( ==), akkor igaz lesz, és ez az eredmény logikus. Itt minden egyszerű.

Kód Konzol kimenet
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Összehasonlítás érték szerint

De gyakran találkozhatunk olyan helyzetekkel, amikor két változó két különböző objektumra utal, amelyek azonosak. Például két különböző karakterlánc-objektum, amelyek ugyanazt a szöveget tartalmazzák.

A módszer segítségével megállapíthatja, hogy a különböző objektumok azonosak-e equals(). Például:

Kód Konzol kimenet
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

A equalsmódszer nem korlátozódik az osztályra String. Minden osztálynak megvan.

Még olyan osztályok is, amelyeket egyedül írsz, és itt van miért.



2. Objectosztály

A Java minden osztálya örökli az Objectosztályt. A Java alkotói ezt a megközelítést találták ki.

És ha egy osztály örökli az Objectosztályt, akkor megkapja az osztály összes metódusát Object. És ez az öröklődés egyik fő következménye.

Más szóval, minden osztály rendelkezik az osztály metódusaival Object, még akkor is, ha a kódjuk nem említi őket.

Ezek az öröklött módszerek magukban foglalják az objektum-összehasonlításhoz kapcsolódó módszereket. Ezek a equals()és hashCode()módszerek.

Kód A valóságban a következőkkel fogunk rendelkezni:
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
   }
}

A fenti példában létrehoztunk egy egyszerű Personosztályt név és életkor paraméterekkel, de nem egyetlen metódussal. De mivel minden osztály örökli az Objectosztályt, az Personosztálynak automatikusan két metódusa van:

Módszer Leírás
boolean equals(Object obj)
Összehasonlítja az aktuális objektumot és az átadott objektumot
int hashCode()
Az aktuális objektum hashkódját adja vissza

Kiderült, hogy abszolút minden objektumnak megvan a equalsmódszere, és a különböző típusú objektumok összehasonlíthatók egymással. Az ilyen kód lefordítható és tökéletesen működik.

Kód Konzol kimenet
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()módszer

Az osztálytól equals()örökölt metódus Objecta legegyszerűbb algoritmust valósítja meg az aktuális objektum és az átadott objektumok összehasonlítására: csak összehasonlítja az objektumokhoz való hivatkozásokat.

Ugyanezt az eredményt kapja, ha csak Persona változókat hasonlítja össze a metódus meghívása helyett equals(). Példa:

Kód Konzol kimenet
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

Amikor a equalsmetódus meghívásra kerül a, egyszerűen összehasonlítja a változóban tárolt referenciát aa változóban tárolt hivatkozással b.

Az összehasonlítás azonban másként működik az osztályban String. Miért?

Mert az emberek, akik létrehozták az Stringosztályt, megírták a saját módszerüket equals().

equals()A módszer megvalósítása

Most írjuk meg az egyenlőség metódus saját implementációját az osztályba Person. 4 fő esetet fogunk megvizsgálni.

Fontos:
Függetlenül attól, hogy melyik osztály írja felül a equalsmetódust, mindig egy objektumot vesz fel Objectargumentumként

1. forgatókönyv : ugyanaz az objektum, amelyen a equalsmetódus meghívásra kerül, szintén átadásra kerül a equalsmetódusnak. Ha az aktuális objektum és az átadott objektum hivatkozásai megegyeznek, a metódusnak vissza kell térnie true. Egy tárgy egyenlő önmagával.

Kódban így fog kinézni:

Kód Leírás
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

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


Hasonlítsa össze a referenciákat

2. forgatókönyv : nullátkerül a equalsmetódushoz – nincs mihez hasonlítanunk. Az objektum, amelyen a metódus meghívódik, biztosan nem null, ezért ebben az esetben equalsvissza kell térnünk .false

Kódban így fog kinézni:

Kód Leírás
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

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


Referenciák összehasonlítása


Az átadott objektum null?

3. forgatókönyv : egy olyan objektumra való hivatkozás, amely nem a, Personátadásra kerül a equalsmetódusnak. Az Personobjektum egyenlő a nem tárggyal Person? Ezt a kérdést az osztály fejlesztőjének Personkell eldöntenie, ahogy akarja.

De általában az objektumoknak ugyanabba az osztályba kell tartozniuk, hogy egyenlőnek tekintsék őket. Ezért, ha az osztály objektumán kívül mást is Personátadunk az equals metódusunknak, akkor mindig a -t adjuk vissza false. Hogyan ellenőrizheti egy objektum típusát? Így van – az operátor használatával instanceof.

Így néz ki az új kódunk:

Kód Leírás
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
}


Referenciák összehasonlítása


Az átadott objektum null?


Ha az átadott objektum nem aPerson

Person4. Két objektum összehasonlítása

Mire jutottunk? Ha a metódus végére értünk, akkor van egy Personobjektumhivatkozásunk, amely nem null. Tehát konvertáljuk a-ba, Personés összehasonlítjuk mindkét objektum releváns belső adatait. És ez a negyedik forgatókönyvünk .

Kód Leírás
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
}


Referenciák összehasonlítása


Az átadott objektum null?


Ha az átadott objektum nem Person


Typecasting

És hogyan lehet összehasonlítani két Persontárgyat? Egyenlőek, ha azonos a nevük ( name) és az életkoruk ( age). A végső kód így fog kinézni:

Kód Leírás
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;
}


Referenciák összehasonlítása


Az átadott objektum null?


Ha az átadott objektum nem Person


Typecasting

De ez még nem minden.

Először is, a név mező egy String, ezért a névmezőt a metódus meghívásával kell összehasonlítani equals.

this.name.equals(person.name)

Másodszor, a namemező lehet null: ebben az esetben nem hívhatja equalsmeg. További ellenőrzésre van szüksége null:

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

Ez azt jelenti, hogy ha a névmező nullmindkét Personobjektumban szerepel, akkor a nevek továbbra is azonosak.

A negyedik forgatókönyv kódja így nézhet ki:

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


Ha az életkorok nem egyenlőek,
azonnal return false

If this.nameegyenlő -val null, akkor nincs értelme a equalsmódszerrel összehasonlítani. Itt vagy nameegyenlő a második mezővel null, vagy nem.

Hasonlítsa össze a két névmezőt a equalsmetódus segítségével.


5. hashCode()módszer

A két objektum összes mezőjének részletes összehasonlítását célzó módszer mellett equalsvan egy másik módszer is, amely pontatlan, de nagyon gyors összehasonlításra használható: hashCode().

Képzelje el, hogy ábécé sorrendben rendezi a több ezer szóból álló listát, és ismételten össze kell hasonlítania a szópárokat. És a szavak hosszúak, sok betűből állnak. Általánosságban elmondható, hogy egy ilyen összehasonlítás nagyon sokáig tartana.

De fel lehet gyorsítani. Tegyük fel, hogy vannak olyan szavaink, amelyek különböző betűkkel kezdődnek – azonnal világos, hogy különböznek. De ha ugyanazokkal a betűkkel kezdődnek, akkor még nem tudjuk megmondani, mi lesz az eredmény: előfordulhat, hogy a szavak azonosak vagy eltérőek.

A hashCode()módszer hasonló elven működik. Ha egy objektumra hívja, akkor valamilyen számot ad vissza – hasonlóan a szó első betűjéhez. Ez a szám a következő tulajdonságokkal rendelkezik:

  • Az azonos objektumoknak mindig ugyanaz a hashcode
  • A különböző objektumok ugyanazzal a hashkóddal rendelkezhetnek, vagy a hashkódjaik eltérőek lehetnek
  • Ha az objektumok különböző hashkódokkal rendelkeznek, akkor az objektumok határozottan mások

Hogy ez még egyértelműbb legyen, fogalmazzuk át ezeket a tulajdonságokat szavakkal:

  • Az azonos szavak kezdőbetűi mindig ugyanazok.
  • Különböző szavak kezdőbetűi lehetnek azonosak, vagy az első betűik eltérőek lehetnek
  • Ha a szavak kezdőbetűi eltérőek, akkor a szavak határozottan mások

Az utolsó tulajdonság az objektumok összehasonlításának felgyorsítására szolgál:

Először a két objektum hashkódját számítjuk ki. Ha ezek a hashkódok különböznek, akkor az objektumok határozottan mások, és nincs szükség további összehasonlításra.

De ha a hashkódok megegyeznek, akkor is össze kell hasonlítanunk az objektumokat az egyenlőség metódusával.



6. Szerződések kódban

A fent leírt viselkedést minden Java osztálynak meg kell valósítania. A fordítás során nincs mód annak ellenőrzésére, hogy az objektumok megfelelően vannak-e összehasonlítva.

A Java programozóknak van egy egyetemes megállapodásuk, miszerint ha saját maguk írják meg az equals() metódus implementációját, és ezzel felülírják a szabványos implementációt (az osztályban Object), akkor a metódus saját implementációját is meg kell írniuk hashCode()úgy, hogy a fent említett szabályok elégedett.

Ezt a megállapodást szerződésnek nevezik .

equals()Ha csak a vagy csak a metódust alkalmazod hashCode()az osztályodban, akkor durván megszeged a szerződést (megszegted a megállapodást). Ne csináld ezt.

Ha más programozók használják az Ön kódját, előfordulhat, hogy nem működik megfelelően. Mi több, olyan kódot fog használni, amely a fenti szerződések betartásán alapul.

Fontos!

Egy elem keresésekor az összes Java gyűjtemény először az objektumok hashkódjait hasonlítja össze, és csak ezután végez összehasonlítást a metódussal equals.

Ez azt jelenti, hogy ha adsz egy equalsmetódust a saját osztályodnak, de nem írod meg a saját metódusodat hashCode(), vagy nem megfelelően implementálod, akkor előfordulhat, hogy a gyűjtemények nem működnek megfelelően az objektumokkal.

Például hozzáadhat egy objektumot egy listához, majd megkeresheti a metódussal contains(), de előfordulhat, hogy a gyűjtemény nem találja meg az objektumot.