— Endi men equals(Object o) & hashCode() metodlari haqida gapirib beraman.
Ehtimol, eslab qolgan bo'lsang kerak, Java-da havola o'zgaruvchilarini taqqoslashda o'zlari emas, balki ob'ektlarga havolalar taqqoslanadi.
Kod | Izoh |
---|---|
|
i j ga teng emas O'zgaruvchilar turli ob'ektlarga ishora qiladi. Garchi ob'ektlar bir xil ma'lumotlarni o'z ichiga oladi; |
|
i j ga teng O'zgaruvchilar bir xil ob'ektga havolani o'z ichiga oladi. |
— Ha, buni eslayman.
— Ushbu vaziyat uchun standart yechim ham bor – bu equals metodi.
Metod equalsning maqsadi – ob'ektlarning ichki tarkibini taqqoslab, ob'ektlarning bir xil ekanligini aniqlash.
— Va bu qanday ishlaydi?
— Bu toString() metodiga o'xshaydi.
Object klassida equals metodining o'z versiyasi mavjud bo'lib, shunchaki havolalarni taqqoslaydi:
public boolean equals(Object obj) {
return (this == obj);
}
— M-da. Nimaga qarshi kurashilgan, unga urildik.
— Yaxshi. Bu metod dasturchilar uni o'z sinfida qayta belgilashlari uchun yaratilgan edi. Axir faqat sinfning dasturchisi qaysi ma'lumotlar muhimligini, nima bilan taqqoslashni va nimani hisobga olmaslikni biladi.
— Shunday metodning misolini keltira olasizmi?
— Albatta. Masalan, bizda matematik kasrlarni tasvirlaydigan sinf bor, u quyidagicha ko'rinadi (aniqlik uchun men inglizcha nomlarni o'zbek tiliga o'g'iraman):
class Drob {
private int surat;
private int maxraj;
Drob(int surat, int maxraj) {
this.surat = surat;
this.maxraj = maxraj;
}
public boolean equals(Object obj) {
if (obj == null)
return false;
if (obj.getClass() != this.getClass())
return false;
Drob other = (Drob) obj;
return this.surat * other.maxraj == this.maxraj * other.surat;
}
}
Misol chaqirish: |
---|
Drob one = new Drob(2,3); Drob two = new Drob(4,6); System.out.println(one.equals(two)); |
Chaqirish natijasi true bo'ladi. 2/3 kasr 4/6 kasrga teng |
— Kattaroq ko'rsatish uchun men o'zbek nomlaridan foydalandim. Buni faqat o'qitish maqsadida qilish mumkin.
Endi misolni tushunamiz.
Biz equals metodini qayta belgiladik va endi Drob sinfining ob'ektlari uchun uning o'z versiyasi mavjud.
Ushbu metodda bir nechta tekshiruv mavjud:
1) Agar taqqoslash uchun o'tkazilgan ob'ekt null bo'lsa, unda ob'ektlar teng emas. Metod equals chaqirilgan ob'ekt aniq null emas.
2) Klasslarni taqqoslashni tekshirish. Agar ob'ektlar turli sinflarga tegishli bo'lsa, ularni taqqoslashni boshlamaymiz va zudlik bilan turli ob'ektlar ekanligini return false deb aytamiz.
3) Ikkinchi sinf maktabdan hamma 2/3 kasr 4/6 kasrga tengligini eslab qoladi. Buni qanday tekshirish mumkin?
2/3 == 4/6 |
---|
Har ikki qismini ikkala maxrajiga (6 va 3) ko'paytiramiz, natija: |
6 * 2 == 4 * 3 |
12 == 12 |
Umumiy qoida: |
Agar a / b == c / d Demak a * d == c * b |
— Shu sababli, equals metodining uchinchi qismida berilgan ob'ektni Drob turiga o'zgartirib, kasrlarni taqqoslaymiz.
— Tushunarli. Agar biz faqat surati surat yoki maxrajni maxraj bilan taqqoslasak, 2/3 kasr 4/6 kasrga teng bo'lmagan bo'lardi.
Endi tushunarli bo'ldi, siz sinf dasturchisi qanday qilib to'g'ri taqqoslashni biladi deb aytgansiz.
— Ha, lekin bu faqat yarim masala. Boshqa bir metod ham borki – hashCode()
— Metod equals tushunarli, lekin hashCode() nima uchun kerak?
— Metod hashCode tez taqqoslash uchun kerak.
Metod equalsning katta kamchiligi bor – u juda sekin ishlaydi. Faraz qilaylik, sizda milliyun elementli Set bor va biz u ma'lum bir ob'ektni o'z ichiga olganmi yoki yo'qligini tekshirishimiz kerak. Buni qanday qilish kerak?
— Kerakli ob'ektni har bir element bilan taqqoslash va kerakli ob'ekt topilgunga qadar barcha elementlarni aylanishdan o'tish mumkin.
— Agar u yerda bo'lmasa-chi? Ushbu ob'ektsiz bir million taqqoslashni amalga oshiramizmi? Judayam ko'p emasmi?
— Ha, hatto menga ham juda ko'p taqqoslashlar bo'lishi ko'rinadi. Boshqa usul mavjudmi?
— Ha, bu uchun hashCode() qo'llaniladi.
Metod hashCode() har bir ob'ekt uchun ma'lum bir sonni qaytaradi. Qanday son – bu ham equals metodida bo'lgani kabi sinfni dasturchisi tomonidan hal qilinadi.
Keling, vaziyatni misol bilan ko'rib chiqaylik:
Faraz qilaylik, sizda milliyon 10 xonali sonlar bor. Keyin hashCode sifatida har bir raqam uchun 100 ga bo'lishdan qoldiqni tanlashimiz mumkin.
Misol:
Raqam | Bizning hashCode |
---|---|
1234567890 | 90 |
9876554321 | 21 |
9876554221 | 21 |
9886554121 | 21 |
— Ha, bu tushunarli. HashCode soni bilan nima qilishimiz kerak?
— Biz raqamlarni taqqoslash o'rniga hashCode-larni taqqoslaymiz. Bu tezroq.
Faqatgina hashCode-lar teng bo'lsa, equals orqali taqqoslaymiz.
— Ha, bu tezroq. Ammo biz hali ham millionta taqqoslashni amalga oshirishimiz kerak, faqat qisqaroq raqamlar bilan, va hashCode-lari bir xil raqamlar uchun yana equals chaqirish kerak bo'ladi.
— Yo'q, kamroq sonlar bilan ham amalga oshirish mumkin.
Faraz qilaylik, bizning Set hashCode bo'yicha guruhlangan yoki hashCode bo'yicha saralangan raqamlarni saqlaydi (bu ularning guruhlanishiga teng, chunki bir xil hashCode ga ega raqamlar yonma-yon joylashgan). Keyin har bir guruh uchun bir marta hashCode belgisining ma'lum bir ob'ektga tengligini tekshirish kifoya qiladi, kerak bo'lmagan guruhlarni juda tez va osonlik bilan chiqarib tashlashimiz mumkin bo'ladi.
Faraz qilaylik, siz talabasisiz va yuzingizni ko'rgan va 17-umumiy yotoqxonada yashashni bilgan do'stingizni qidiryapsiz. Keyin siz universitetdagi barcha yotoqxonalarni aylanib chiqasiz va har bir yotoqxonada "bu 17-yotoqxona?" deb so'raysiz. Agar yo'q bo'lsa, ushbu yotoqxonadagi hamma odamlarni chiqarib tashlaysiz va keyingisiga o'tasiz. Agar "ha" bo'lsa, barcha xonalardan o'tib, do'stingizni izlashni boshlaysiz.
Ushbu misolda yotoqxona raqami – 17 – bu hashCode.
HashCode funktsiyasini amalga oshiruvchi dasturchi quyidagi narsalarni bilishi kerak:
A) turli ob'ektlar bir xil hashCode ga ega bo'lishi mumkin (turli odamlar bitta yotoqxonada yashashi mumkin)
B) bir xil ob'ektlar (equals nuqtai nazaridan) bir xil hashCode ga ega bo'lishi kerak.
V) hash-kodlar turli ob'ektlarning katta miqdoriga ega bo'lmasligi uchun tanlanishi kerak. Bu ularning barcha afzalliklarini bekor qiladi (Siz 17-yotoqxonaga keldingiz va u yerda yarim universitet yashaydi. Abla).
Endi eng muhim narsa. Agar equals metodini qayta belgilasangiz, hashCode metodini ham qayta belgilashingiz kerak, uchta yuqoridagi qoidalarni inobatga olib.
Barcha masala Java kolleksiyalarida ob'ektlarni taqqoslashdan oldin doimo hashCode() metodidan foydalanib tekshiriladi/taqqoslanadi. Va agar bir xil ob'ektlarda turli hashCode bo'lsa, ob'ektlar turli ko'rinadi - equals orqali taqqoslashga o'tishga imkon bermaydi.
Bizning misolimizda Drob bilan, agar biz hashCode ni suratga teng olsak, unda 2/3 va 4/6 kasrlari turli hashCode ga ega bo'ladi. Kasrlar bir xil, equals ularni bir xil deb aytadi, lekin hashCode ularni turli deb aytadi. Agar equals orqali taqqoslashdan oldin hashCode bo'yicha taqqoslasak, ob'ektlar turli ekanligini olishamiz va equals ga hech qachon o'tmaymiz.
Misol:
HashSet<Drob>set = new HashSet<Drob>(); set.add(new Drob(2,3)); System.out.println( set.contains(new Drob(4,6)) ); |
Agar hashCode() metodi kasrning suratini qaytarsa, natija false bo'ladi. Ob'ekt new Drob(4,6) kollektsiyada topilmaydi. |
— Kasrlar uchun hashCode ni qanday to'g'ri amalga oshirish mumkin?
— Bu erda bir xil kasrlarda hashCode mutlaqo bir xil bo'lishi kerakligini eslash kerak.
Variant 1: hashCode butun qismining bo'linishiga teng.
7/5 va 6/5 kasrlari uchun bu 1 bo'ladi.
4/5 va 3/5 kasrlari uchun bu 0 bo'ladi.
Lekin bu variant 1 dan kichik bo'lgan kasrlarni taqqoslash uchun yaramaydi. Butun qismi (hashCode) doim 0 bo'ladi.
Variant 2: hashCode butun qismining maxrajning suratiga bo'linishiga teng.
Bu variant 1 dan kichik bo'lgan kasr qiymatini ko'rsatuvchi holat uchun mos keladi. Agar kasr 1 dan kichik bo'lsa, demak teskari kasr 1 dan katta bo'ladi. Va agar biz barcha kasrlarni ag'darsak - bu ularning taqqoslanishiga hech qanday ta'sir ko'rsatmaydi.
Yakuniy variant har ikki echimni birlashtiradi:
public int hashCode() {
return surat/maxraj + maxraj/surat;
}
2/3 va 4/6 kasrlari uchun hashCode ni tekshiramiz. Ular bir xil hashCode ga ega bo'lishi kerak:
2/3 Kasr | 4/6 Kasr | |
---|---|---|
surat / maxraj | 2 / 3 == 0 | 4 / 6 == 0 |
maxraj / surat | 3 / 2 == 1 | 6 / 4 == 1 |
surat / maxraj + maxraj / surat |
0 + 1 == 1 | 0 + 1 == 1 |
Bu bilan – hammasi.
— Rahmat, Elli, bu haqiqatan ham qiziq edi.
GO TO FULL VERSION