Metode hashcode () dan setara () dapat dikatakan sebagai fitur utama dari Java yang sepenuhnya berorientasi objek. Ini memfasilitasi pemrograman kami dan juga membawa banyak bahaya. Dalam artikel ini, kita akan membahas cara memahami dan menggunakan dua metode ini dengan benar.
Jika Anda memutuskan untuk menulis ulang metode Equals (), maka Anda harus jelas tentang risiko yang dibawa dengan melakukannya dan pastikan Anda dapat menulis metode Equals () yang kuat. Satu hal yang harus Anda perhatikan adalah bahwa setelah penulisan ulang equals (), Anda harus menulis ulang metode hashcode (). Alasan spesifik akan dijelaskan nanti.
Pertama -tama mari kita lihat deskripsi metode Equals () dalam spesifikasi Javase 7:
・ Ini refleksif: Untuk nilai referensi non-null x, x.equals(x) harus mengembalikan true .
・ Ini simetris: untuk nilai referensi non-null x dan y, x.equals(y) harus mengembalikan true jika dan hanya jika y.equals(x) mengembalikan true .
・ Ini transitif: Untuk nilai referensi non-null x, y, dan z, jika x.equals(y) mengembalikan true dan y.equals(z) mengembalikan true , maka x.equals(z) harus mengembalikan true.
・ Konsisten: untuk nilai referensi non-nol x dan y , beberapa doa x.equals(y) secara konsisten mengembalikan true atau secara konsisten mengembalikan false , asalkan tidak ada informasi yang digunakan dalam sama dengan perbandingan pada objek yang dimodifikasi.
・ Untuk nilai referensi non-null x, x.equals(null) harus mengembalikan false .
Perikop ini menggunakan banyak numerologi dalam matematika diskrit. Izinkan saya memberikan penjelasan singkat:
1. Refleksivitas: A.Equals (a) harus mengembalikan true.
2. Simetri: Jika A.Equals (b) mengembalikan true, maka B.Equals (a) juga perlu mengembalikan true.
3. Transmisi: Jika A.Equals (b) adalah benar dan b.equals (c) adalah benar, maka A.Equals (c) juga harus benar. Terus terang, a = b, b = c, lalu a = C.
4. Konsistensi: Selama keadaan objek A dan B tidak berubah, A.Equals (b) harus selalu mengembalikan benar.
5. A.Equals (null) untuk mengembalikan false.
Saya percaya bahwa selama orang yang tidak profesional dalam matematika tidak akan menyebut hal -hal di atas. Dalam aplikasi aktual, kita hanya perlu menulis ulang metode Equals () sesuai dengan langkah -langkah tertentu. Untuk kenyamanan penjelasan, pertama -tama kami mendefinisikan kelas programmer (coder):
class coder {private string name; usia int pribadi; // Getters and Setters}Yang kami inginkan adalah bahwa jika nama dan usia dari kedua objek programmer adalah sama, maka kami berpikir bahwa kedua pemrogram ini sama. Pada saat ini, kita harus menulis ulang metode Equals () -nya. Karena default equals () sebenarnya menentukan apakah dua referensi menunjuk ke objek yang sama intrinsik, itu setara dengan ==. Saat menulis ulang, ikuti tiga langkah berikut:
1. Tentukan apakah itu sama dengan diri Anda sendiri.
if (lain == ini) mengembalikan true;
2. Gunakan instance dari operator untuk menentukan apakah yang lain adalah objek tipe coder.
if (! (Contoh lain dari coder)) mengembalikan false;
3. Bandingkan domain data, nama dan usia yang Anda sesuaikan di kelas coder, dan Anda tidak boleh melewatkannya.
Coder o = (coder) lainnya; return o.name.equals (name) && o.age == usia;
Melihat ini, seseorang mungkin bertanya, ada pemeran di Langkah 3. Jika seseorang meneruskan objek kelas Integer ke dalam setara ini, akankah ia melempar ClassCastException? Kekhawatiran ini sebenarnya berlebihan. Karena kami telah membuat penilaian contoh pada langkah kedua, jika yang lain adalah objek non-coder, atau bahkan yang lain adalah nol, maka false akan dikembalikan secara langsung pada langkah ini, sehingga kode selanjutnya tidak akan mendapatkan kesempatan untuk dieksekusi.
Tiga langkah di atas juga merupakan langkah yang direkomendasikan dalam <Java> yang efektif, yang pada dasarnya dapat memastikan bahwa tidak ada kesalahan.
Dalam spesifikasi Javase 7,
"Perhatikan bahwa umumnya diperlukan untuk mengganti metode kode hash setiap kali metode ini (sama) ditimpa, sehingga dapat mempertahankan kontrak umum untuk metode kode hash, yang menyatakan bahwa objek yang sama harus memiliki kode hash yang sama."
Jika Anda menulis ulang metode Equals (), maka ingatlah untuk menulis ulang metode hashcode (). Kami telah mempelajari tabel hash di kursus struktur data komputer universitas. Metode hashcode () melayani tabel hash.
Ketika kami menggunakan kelas koleksi yang dimulai dengan hash seperti hash, seperti hashmap dan hashset, hashcode () akan disebut secara implisit untuk membuat hubungan pemetaan hash. Kami akan menjelaskan ini nanti. Di sini kita akan fokus pada penulisan metode hashcode () terlebih dahulu.
<Java> yang efektif menyediakan metode penulisan yang dapat menghindari konflik hash sejauh ini, tetapi saya pribadi berpikir bahwa tidak perlu melakukan begitu banyak masalah untuk aplikasi umum. Jika Anda perlu menyimpan puluhan ribu atau jutaan objek dalam aplikasi Anda, Anda harus mengikuti metode yang diberikan dalam buku ini. Jika Anda menulis aplikasi kecil dan menengah, maka prinsip-prinsip berikut sudah cukup:
Penting untuk memastikan bahwa semua anggota objek coder dapat tercermin dalam kode hash.
Untuk contoh ini, kita dapat menulis ini:
@Override public int hashCode () {int result = 17; hasil = hasil * 31 + name.hashCode (); Hasil = Hasil * 31 + Usia; hasil pengembalian; }Di mana hasil int = 17 Anda juga dapat mengubahnya menjadi 20, 50, dll. Melihat ini, saya tiba -tiba penasaran dan ingin melihat bagaimana metode hashcode () di kelas string diimplementasikan. Periksa dokumentasi dan ketahuilah:
"Mengembalikan kode hash untuk string ini. Kode hash untuk objek string dihitung sebagai
S [0]*31^(n-1) + S [1]*31^(n-2) + ... + S [n-1]
Menggunakan aritmatika int, di mana s [i] adalah karakter ith dari string, n adalah panjang string, dan ^ menunjukkan eksponensial. (Nilai hash dari string kosong adalah nol.) "
Hitung kode ASCII dari masing -masing karakter ke daya n - 1 dan kemudian tambahkan. Dapat dilihat bahwa matahari sangat ketat dalam implementasi kode hash. Ini dapat menghindari kode hash yang sama di dua string yang berbeda sampai tingkat terbesar.
Konsep bucket dirujuk dalam implementasi tabel hash Oracle. Seperti yang ditunjukkan pada gambar di bawah ini:
Seperti yang dapat dilihat dari gambar di atas, tabel hash dengan ember kira -kira setara dengan kombinasi tabel hash dan daftar tertaut. Artinya, daftar yang ditautkan akan digantung pada setiap ember, dan setiap simpul dari daftar yang ditautkan akan digunakan untuk menyimpan objek. Java menggunakan metode hashcode () untuk menentukan bucket mana objek yang harus ditempatkan, dan kemudian mencarinya di daftar tertaut yang sesuai. Idealnya, jika metode hashcode () Anda tertulis cukup kuat, maka setiap ember hanya akan memiliki satu node, yang akan mencapai kompleksitas waktu tingkat konstan dari operasi pencarian. Artinya, tidak peduli bagian memori mana objek Anda ditempatkan, saya dapat segera menemukan area melalui hashcode () tanpa melintasi dan mencari dari awal hingga akhir. Ini juga merupakan aplikasi utama tabel hash.
menyukai:
Ketika kami memanggil metode hashset put (objek O), pertama -tama kami akan menemukannya di ember yang sesuai sesuai dengan nilai pengembalian O.HashCode (). Jika tidak ada node di ember, maka letakkan o di sini. Jika sudah ada node, maka gantung O ke ujung daftar yang ditautkan. Demikian pula, ketika panggilan berisi (objek O), Java akan menemukan ember yang sesuai melalui nilai pengembalian hashCode (), dan kemudian memanggil metode Equals () pada gilirannya pada node dalam daftar tertaut yang sesuai untuk menentukan apakah objek dalam node adalah objek yang Anda inginkan.
Mari kita gunakan contoh untuk mengalami proses ini:
Mari kita buat dua objek coder baru terlebih dahulu:
Coder c1 = coder baru ("Bruce", 10); Coder c2 = coder baru ("Bruce", 10);Asumsikan bahwa kami telah menulis ulang metode equals () coder tanpa menulis ulang metode hashcode ():
@Override Public Boolean Equals (objek lain) {System.out.println ("Metode Equals Dipanggil!"); if (lain == ini) mengembalikan true; if (! (Contoh lain dari coder)) mengembalikan false; Coder o = (coder) lainnya; return o.name.equals (name) && o.age == usia; }Kemudian kami membangun hashset dan memasukkan objek C1 ke dalam set:
Set <coder> set = hashset baru <coder> (); set.add (C1);
Jalankan lagi:
System.out.println (set.Contains (C2));
Kami mengharapkan metode contains (C2) untuk mengembalikan true, tetapi pada kenyataannya itu kembali salah.
Nama dan usia C1 dan C2 adalah sama. Mengapa saya menelepon berisi (C2) dan mengembalikan false setelah memasukkan C1 ke dalam hashset? Ini adalah hashcode () yang menyebabkan masalah. Karena Anda belum menulis ulang metode hashcode (), ketika hashset mencari C2, itu akan mencarinya dalam ember yang berbeda. Misalnya, jika C1 dimasukkan ke dalam ember 05, dicari di bucket 06 saat mencari C2, jadi tentu saja tidak dapat ditemukan. Oleh karena itu, tujuan hashcode penulisan ulang kami () adalah bahwa ketika A.Equals (b) mengembalikan true, kode hashcode () dari A dan B harus mengembalikan nilai yang sama.
Apakah saya meminta hashcode () untuk mengembalikan baris nomor tetap setiap saat
Seseorang mungkin menulis ulang seperti ini:
@Override public int hashCode () {return 10; }Jika ini masalahnya, hashmap, hashset dan kelas koleksi lainnya akan kehilangan "makna hash" mereka. Dalam kata -kata <java efektif>, tabel hash merosot menjadi daftar yang ditautkan. Jika hashCode () mengembalikan nomor yang sama setiap kali, maka semua objek akan ditempatkan di ember yang sama, dan setiap kali Anda melakukan operasi pencarian, itu akan melintasi daftar yang ditautkan, yang sepenuhnya akan kehilangan fungsi hashing. Jadi lebih baik memberikan kode hashcode yang kuat () sebagai ide yang bagus.
Di atas adalah semua pengenalan terperinci dari artikel ini tentang metode hashcode () dan equals (). Saya harap ini akan membantu semua orang. Teman yang tertarik dapat terus merujuk ke topik terkait lainnya di situs ini. Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya. Terima kasih teman atas dukungan Anda untuk situs ini!