"Sekarang saya akan memberitahu anda tentang beberapa kaedah yang sama membantu: equals(Object o) & hashCode() ."
"Anda mungkin sudah ingat bahawa, di Jawa, apabila membandingkan pembolehubah rujukan, objek itu sendiri tidak dibandingkan, sebaliknya rujukan kepada objek."
Kod | Penjelasan |
---|---|
|
i tidak sama dengan j Pembolehubah menghala ke objek yang berbeza. Walaupun objek mengandungi data yang sama. |
|
i sama dengan j. Pembolehubah mengandungi rujukan kepada objek yang sama. |
"Ya, saya ingat itu."
Yang setaraf .
"Kaedah equals ialah penyelesaian standard di sini. Tujuan kaedah equals adalah untuk menentukan sama ada objek secara dalaman adalah sama dengan membandingkan apa yang disimpan di dalamnya."
"Dan bagaimana ia melakukannya?""Semuanya hampir sama dengan kaedah toString()."
Kelas Objek mempunyai pelaksanaan sendiri kaedah sama, yang hanya membandingkan rujukan:
public boolean equals(Object obj)
{
return (this == obj);
}
"Hebat... kembali kepada itu lagi, kan?"
"Tegakkan dagu anda! Ia sebenarnya sangat rumit."
"Kaedah ini dicipta untuk membolehkan pembangun menimpanya dalam kelas mereka sendiri. Lagipun, hanya pembangun kelas yang tahu apa data yang berkaitan dan apa yang tidak apabila membandingkan."
"Bolehkah anda memberi contoh?"
"Tentu. Katakan kita mempunyai kelas yang mewakili pecahan matematik. Ia akan kelihatan seperti ini:"
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;
}
}
Contoh panggilan kaedah: |
---|
Fraction one = new Fraction(2,3); Fraction two = new Fraction(4,6); System.out.println(one.equals(two)); |
Panggilan kaedah akan kembali benar. Pecahan 2/3 adalah sama dengan pecahan 4/6 |
"Sekarang, mari kita membedah contoh ini."
"Kami mengatasi kaedah equals , jadi objek Fraction akan mempunyai pelaksanaannya sendiri.
"Terdapat beberapa semakan dalam kaedah:"
" 1) Jika objek yang diluluskan untuk perbandingan adalah null , maka objek tersebut tidak sama. Jika anda boleh memanggil kaedah equals pada objek, maka ia pasti bukan null ."
" 2) Perbandingan kelas. Jika objek adalah contoh kelas yang berbeza, maka kami tidak akan cuba membandingkannya. Sebaliknya, kami akan segera menggunakan return false untuk menunjukkan bahawa ini adalah objek yang berbeza."
" 3) Semua orang ingat dari darjah dua bahawa 2/3 bersamaan dengan 4/6. Tetapi bagaimana anda menyemaknya?"
2/3 == 4/6 |
---|
Kami mendarab kedua-dua belah dengan kedua-dua pembahagi (6 dan 3), dan kami mendapat: |
6 * 2 == 4 * 3 |
12 == 12 |
Peraturan Am: |
Jika a / b == c / d Kemudian a * d == c * b |
"Oleh itu, dalam bahagian ketiga kaedah sama , kami membuang objek yang diluluskan kepada Pecahan dan membandingkan pecahan."
"Faham. Jika kita hanya membandingkan pengangka dengan pengangka dan penyebut dengan penyebut, maka 2/3 tidak sama dengan 4/6."
"Sekarang saya faham apa yang anda maksudkan apabila anda mengatakan bahawa hanya pembangun kelas yang tahu cara membandingkannya dengan betul."
"Ya, tetapi itu hanya separuh daripada cerita. Terdapat kaedah lain: hashCode(). "
"Segala-galanya tentang kaedah equals masuk akal sekarang, tetapi mengapa kita memerlukan hashCode ()? "
" Kaedah hashCode diperlukan untuk perbandingan pantas."
" Kaedah equals mempunyai kelemahan utama: ia berfungsi terlalu perlahan. Katakan anda mempunyai Set berjuta-juta elemen dan perlu menyemak sama ada ia mengandungi objek tertentu. Bagaimana anda melakukannya?"
"Saya boleh mengitari semua elemen menggunakan gelung dan membandingkan objek dengan setiap objek dalam set. Sehingga saya menemui padanan."
"Dan jika ia tidak ada? Kami akan melakukan sejuta perbandingan hanya untuk mengetahui objek itu tiada di sana? Bukankah itu kelihatan seperti banyak?"
"Ya, walaupun saya sedar itu terlalu banyak perbandingan. Adakah ada cara lain?"
"Ya, anda boleh menggunakan hashCode () untuk ini.
Kaedah hashCode () mengembalikan nombor tertentu untuk setiap objek. Pembangun kelas menentukan nombor yang dikembalikan, sama seperti yang dia lakukan untuk kaedah yang sama.
"Mari kita lihat contoh:"
"Bayangkan anda mempunyai sejuta nombor 10 digit. Kemudian, anda boleh menjadikan kod cincang setiap nombor sebagai baki selepas membahagikan nombor itu dengan 100."
Berikut ialah contoh:
Nombor | Kod cincang kami |
---|---|
1234567890 | 90 |
9876554321 | 21 |
9876554221 | 21 |
9886554121 | 21 |
"Ya, itu masuk akal. Dan apa yang kita lakukan dengan Kod cincang ini?"
"Daripada membandingkan nombor, kami membandingkan Kod cincang mereka . Ia lebih pantas seperti itu."
"Dan kami memanggil sama hanya jika Kod cincang mereka adalah sama."
"Ya, itu lebih pantas. Tetapi kami masih perlu membuat sejuta perbandingan. Kami hanya membandingkan nombor yang lebih kecil, dan kami masih perlu memanggil yang sama untuk sebarang nombor dengan Kod cincang yang sepadan."
"Tidak, anda boleh lari dengan jumlah perbandingan yang jauh lebih kecil."
"Bayangkan Set kami menyimpan nombor yang dikumpulkan atau diisih mengikut Kod cincang (mengisihnya dengan cara ini pada asasnya mengumpulkannya, kerana nombor dengan Kod cincang yang sama akan bersebelahan antara satu sama lain). Kemudian anda boleh dengan cepat dan mudah membuang kumpulan yang tidak berkaitan. Itu sudah memadai untuk menyemak sekali bagi setiap kumpulan untuk melihat sama ada Kod cincangnya sepadan dengan Kod cincang objek."
"Bayangkan anda seorang pelajar mencari kawan yang anda boleh kenali dengan penglihatan dan yang kami tahu tinggal di Dorm 17. Kemudian anda hanya pergi ke setiap asrama di universiti dan bertanya, 'Adakah ini Dorm 17?' Jika tidak, maka awak abaikan semua orang di asrama dan teruskan ke seterusnya. Jika jawapannya 'ya', maka awak mula berjalan melepasi setiap bilik, mencari kawan awak."
"Dalam contoh ini, nombor asrama (17) ialah Kod cincang."
"Pemaju yang melaksanakan fungsi hashCode mesti mengetahui perkara berikut:"
A) dua objek berbeza boleh mempunyai Kod hash yang sama (orang yang berbeza boleh tinggal di asrama yang sama)
B) objek yang sama ( mengikut kaedah yang sama ) mesti mempunyai Kod cincang yang sama. .
C) kod cincang mesti dipilih supaya tidak terdapat banyak objek berbeza dengan Kod cincang yang sama. Jika ada, maka kelebihan potensi kod cincang hilang (Anda sampai ke Asrama 17 dan mendapati separuh universiti tinggal di sana. Bummer!).
"Dan sekarang perkara yang paling penting. Jika anda mengatasi kaedah equals , anda mesti mengatasi kaedah hashCode () dan mematuhi tiga peraturan yang diterangkan di atas.
"Alasannya ialah: dalam Java, objek dalam koleksi sentiasa dibandingkan/diambil menggunakan hashCode() sebelum ia dibandingkan/diambil menggunakan equals. Dan jika objek yang sama mempunyai hashCodes yang berbeza, maka objek akan dianggap berbeza dan kaedah yang sama. tidak akan dipanggil pun.
"Dalam contoh Pecahan kami, jika kami membuat Kod cincang sama dengan pengangka, pecahan 2/3 dan 4/6 akan mempunyai Kod cincang yang berbeza. Pecahan adalah sama dan kaedah sama mengatakan ia adalah sama, tetapi Kod cincang mereka mengatakan mereka berbeza. Dan jika kita membandingkan menggunakan Kod hash sebelum kita membandingkan menggunakan sama, maka kita menyimpulkan bahawa objek adalah berbeza dan kita tidak pernah sampai ke kaedah sama."
Berikut ialah contoh:
HashSet<Fraction>set = new HashSet<Fraction>(); set.add(new Fraction(2,3)); System.out.println( set.contains(new Fraction(4,6)) ); |
Jika kaedah hashCode() mengembalikan pengangka pecahan, hasilnya akan palsu . Dan objek « Pecahan baru(4,6) » tidak akan ditemui dalam koleksi. |
"Jadi apakah cara yang betul untuk melaksanakan Kod hash untuk pecahan?"
"Di sini anda perlu ingat bahawa pecahan setara mesti mempunyai Kod cincang yang sama."
" Versi 1 : Kod cincang sama dengan hasil pembahagian integer."
"Untuk 7/5 dan 6/5, ini akan menjadi 1."
"Untuk 4/5 dan 3/5, ini akan menjadi 0."
"Tetapi pilihan ini kurang sesuai untuk membandingkan pecahan yang sengaja kurang daripada 1. Kod cincang (hasil pembahagian integer) akan sentiasa 0."
" Versi 2 : Kod hash sama dengan hasil pembahagian integer penyebut oleh pengangka."
"Pilihan ini sesuai untuk keadaan di mana pecahan kurang daripada 1. Jika pecahan kurang daripada 1, maka songsangannya lebih besar daripada 1. Dan jika kita menyongsangkan semua pecahan, maka perbandingan tidak akan terjejas."
"Versi akhir kami menggabungkan kedua-dua penyelesaian:"
public int hashCode()
{
return numerator/denominator + denominator/numerator;
}
Mari kita uji menggunakan 2/3 dan 4/6. Mereka sepatutnya mempunyai Kod hash yang sama:
Pecahan 2/3 | Pecahan 4/6 | |
---|---|---|
pengangka / penyebut | 2/3 == 0 | 4 / 6 == 0 |
penyebut / pengangka | 3/2 == 1 | 6/4 == 1 |
pengangka / penyebut + penyebut / pengangka |
0 + 1 == 1 | 0 + 1 == 1 |
"Itu sahaja buat masa ini."
"Terima kasih, Ellie. Itu sangat menarik."
GO TO FULL VERSION