"Most elmondok néhány olyan módszert, amelyek ugyanolyan hasznosak: equals(Object o) & hashCode() ."
"Valószínűleg már emlékezett rá, hogy a Java-ban a referenciaváltozók összehasonlításakor nem magukat az objektumokat hasonlítják össze, hanem az objektumokra való hivatkozásokat."
Kód | Magyarázat |
---|---|
|
i nem egyenlő j-vel A változók különböző objektumokra mutatnak. Annak ellenére, hogy az objektumok ugyanazokat az adatokat tartalmazzák. |
|
i egyenlő j-vel. A változók ugyanarra az objektumra utalnak. |
– Igen, erre emlékszem.
Az egyenlők .
"Az egyenlőség módszer itt a standard megoldás. Az egyenlőség módszer célja annak meghatározása, hogy az objektumok belsőleg azonosak-e a bennük tárolt adatok összehasonlításával."
– És hogyan teszi ezt?"Ez mind nagyon hasonlít a toString() metódushoz."
Az Object osztálynak megvan a maga egyenlős metódus implementációja, amely egyszerűen összehasonlítja a hivatkozásokat:
public boolean equals(Object obj)
{
return (this == obj);
}
– Remek... térjünk vissza erre, ugye?
"Tartsd fel az állad! Ez valójában nagyon trükkös."
"Ezt a módszert azért hozták létre, hogy a fejlesztők felülírhassák a saját osztályaikban. Végül is csak az osztály fejlesztője tudja, hogy az összehasonlítás során mely adatok relevánsak és melyek nem."
– Tudsz példát mondani?
"Persze. Tegyük fel, hogy van egy osztályunk, amely matematikai törteket reprezentál. Így nézne ki:"
class Fraction
{
private int numerator;
private int denominator;
Fraction(int numerator, int denominator)
{
this.numerator = numerator;
this.denominator = denominator;
}public boolean equals(Object obj)
{
if (obj==null)
return false;
if (obj.getClass() != this.getClass() )
return false;
Fraction other = (Fraction) obj;
return this.numerator* other.denominator == this.denominator * other.numerator;
}
}
Példa metódushívásra: |
---|
Fraction one = new Fraction(2,3); Fraction two = new Fraction(4,6); System.out.println(one.equals(two)); |
A metódushívás true értéket ad vissza. A 2/3 tört egyenlő a 4/6 törttel |
– Most pedig boncolgassuk ezt a példát.
"Felülírtuk az egyenlőség metódust, így a tört objektumoknak saját megvalósításuk lesz.
"Több ellenőrzés is van a módszerben:"
" 1) Ha az összehasonlításra átadott objektum null , akkor az objektumok nem egyenlőek. Ha egy objektumon meghívhatja az equals metódust, akkor az biztosan nem null ."
" 2) Osztály-összehasonlítás. Ha az objektumok különböző osztályok példányai, akkor nem próbáljuk meg összehasonlítani őket. Ehelyett azonnal a return false-t használjuk annak jelzésére, hogy ezek különböző objektumok."
" 3) Mindenki emlékszik a második osztálytól, hogy a 2/3 egyenlő 4/6-tal. De hogyan lehet ezt ellenőrizni?"
2/3 == 4/6 |
---|
Mindkét oldalt megszorozzuk mindkét osztóval (6 és 3), és a következőt kapjuk: |
6 * 2 == 4 * 3 |
12 == 12 |
Általános szabály: |
Ha a / b == c / d Akkor a * d == c * b |
"Ennek megfelelően az egyenlőségi módszer harmadik részében az átadott objektumot törtté öntjük, és összehasonlítjuk a törteket."
"Értem. Ha egyszerűen összehasonlítjuk a számlálót a számlálóval és a nevezőt a nevezővel, akkor a 2/3 nem egyenlő a 4/6-tal."
"Most már értem, mire gondoltál, amikor azt mondtad, hogy csak egy osztály fejlesztője tudja, hogyan kell helyesen összehasonlítani."
"Igen, de ez csak a történet fele. Van egy másik módszer: hashCode() .
"Az egyenlőség metódussal kapcsolatban most mindennek van értelme, de miért van szükségünk a hashCode-ra ()? "
"A hashCode metódus szükséges a gyors összehasonlításhoz."
"Az egyenlőség metódusának van egy nagy hátránya: túl lassan működik. Tegyük fel, hogy több millió elemből álló halmaza van, és ellenőriznie kell, hogy tartalmaz-e egy adott objektumot. Hogyan kell ezt megtenni?"
"Válogathatok az összes elemen egy hurok segítségével, és összehasonlíthatom az objektumot a készlet minden egyes objektumával. Amíg nem találok egyezőt."
"És ha nincs ott? Millió összehasonlítást végeznénk csak azért, hogy megtudjuk, az objektum nincs ott? Nem tűnik soknak?"
– Igen, még én is elismerem, hogy ez túl sok összehasonlítás. Van más mód?
"Igen, használhatja a hashCode- ot () ehhez.
A hashCode () metódus minden objektumhoz egy adott számot ad vissza. Egy osztály fejlesztője dönti el, hogy milyen számot adjon vissza, akárcsak az egyenlő metódus esetében.
– Nézzünk egy példát:
"Képzeld el, hogy van egy millió 10 jegyű számod. Ezután beállíthatod, hogy minden szám hashCode-ja legyen a maradék, miután elosztod a számot 100-zal."
Íme egy példa:
Szám | A mi hashCode-unk |
---|---|
1234567890 | 90 |
9876554321 | 21 |
9876554221 | 21 |
9886554121 | 21 |
"Igen, ennek van értelme. És mit kezdjünk ezzel a hashCode-dal?"
"A számok összehasonlítása helyett a hashCode- jukat hasonlítjuk össze . Így gyorsabb."
"És csak akkor hívjuk egyenlőnek , ha a hashCode- juk egyenlő."
"Igen, ez gyorsabb. De még mindig milliónyi összehasonlítást kell végeznünk. Csak kisebb számokat hasonlítunk össze, és továbbra is egyenlőt kell hívnunk minden olyan számhoz, amelynek hashCode-ja megegyezik."
– Nem, sokkal kisebb számú összehasonlítással megúszhatod.
"Képzeld el, hogy a készletünk a hashCode szerint csoportosítva vagy rendezve tárolja a számokat (az ilyen rendezés lényegében csoportosítást jelent, hiszen az azonos hashCode-val rendelkező számok egymás mellett lesznek). Ekkor nagyon gyorsan és egyszerűen el tudod dobni a nem releváns csoportokat. Elég. csoportonként egyszer ellenőrizze, hogy a hashCode egyezik-e az objektum hashCode-jával."
"Képzeld el, hogy diák vagy, aki egy barátot keres, akit látásból felismerhetsz, és akiről tudjuk, hogy a 17-es kollégiumban él. Aztán csak bemész az egyetem minden kollégiumába, és megkérdezed: "Ez a 17-es kollégium?" Ha nem, akkor figyelmen kívül hagysz mindenkit a kollégiumban, és továbblépsz a következőre. Ha a válasz "igen", akkor elindulsz az egyes szobák mellett, és a barátodat keresed."
"Ebben a példában a kollégium száma (17) a hashCode."
"A hashCode függvényt megvalósító fejlesztőnek tudnia kell a következőket:"
A) két különböző objektumnak lehet ugyanaz a hashCode (különböző emberek élhetnek ugyanabban a kollégiumban)
B) az azonos objektumoknak ( az egyenlőség metódus szerint ) azonos hashCode-kal kell rendelkezniük. .
C) A hash kódokat úgy kell megválasztani, hogy ne legyen sok különböző objektum ugyanazzal a hashCode-dal. Ha vannak, akkor a hashcode-ok potenciális előnyei elvesznek (a 17-es kollégiumba jutsz, és azt tapasztalod, hogy az egyetem fele ott lakik. Baj!).
"És most a legfontosabb dolog. Ha felülírja az egyenlő metódust, akkor feltétlenül felül kell írnia a hashCode () metódust, és be kell tartania a fent leírt három szabályt.
"Az ok a következő: Java-ban a gyűjtemény objektumai mindig a hashCode() segítségével kerülnek összehasonlításra/lekérésre, mielőtt összehasonlítják/lekérik őket az egyenlő értékkel. Ha pedig az azonos objektumoknak különböző hashCode-jaik vannak, akkor az objektumokat különbözőnek tekinti, és az egyenlő metódus nem is hívják.
"A Tört példánkban, ha a hashCode-ot egyenlővé tesszük a számlálóval, akkor a 2/3 és 4/6 törtek különböző hashCode-okkal rendelkeznének. A törtek azonosak, és az egyenlőség metódus szerint ugyanazok, de a hashCode-juk szerint És ha összehasonlítjuk a hashCode használatával, mielőtt összehasonlítanánk az egyenlők használatával, akkor arra a következtetésre jutunk, hogy az objektumok különböznek egymástól, és soha nem jutunk el az egyenlő metódushoz."
Íme egy példa:
HashSet<Fraction>set = new HashSet<Fraction>(); set.add(new Fraction(2,3)); System.out.println( set.contains(new Fraction(4,6)) ); |
Ha a hashCode() metódus visszaadja a törtek számlálóját, az eredmény false lesz . És az «új Tört(4,6) » objektum nem lesz megtalálható a gyűjteményben. |
"Szóval mi a helyes módja a hashCode megvalósításának a törteknél?"
"Itt emlékeznie kell arra, hogy az egyenértékű törteknek ugyanazzal a hashCode-val kell rendelkezniük."
" 1. verzió : a hashCode megegyezik az egész számok osztásának eredményével."
"7/5 és 6/5 esetében ez 1 lenne."
"4/5 és 3/5 esetében ez 0 lenne."
"De ez az opció nem alkalmas olyan törtek összehasonlítására, amelyek szándékosan kisebbek 1-nél. A hashCode (az egész számok felosztásának eredménye) mindig 0 lesz."
" 2. verzió : a hashCode megegyezik a nevező számlálóval való egész számmal való osztásának eredményével."
"Ez az opció olyan esetekben megfelelő, amikor a tört kisebb, mint 1. Ha a tört kisebb, mint 1, akkor az inverze nagyobb, mint 1. És ha az összes törtet megfordítjuk, akkor az összehasonlításokat ez semmilyen módon nem érinti."
"Végső változatunk mindkét megoldást egyesíti:"
public int hashCode()
{
return numerator/denominator + denominator/numerator;
}
Teszteljük 2/3 és 4/6 segítségével. Azonos hashCode-okkal kell rendelkezniük:
2/3 | 4/6 | |
---|---|---|
számláló / nevező | 2/3 == 0 | 4/6 == 0 |
nevező / számláló | 3/2 == 1 | 6/4 == 1 |
számláló / nevező + nevező / számláló |
0 + 1 == 1 | 0 + 1 == 1 |
"Ez minden most."
– Köszönöm, Ellie. Ez nagyon érdekes volt.
GO TO FULL VERSION