Metode yang sama dan metode kode hash dalam java ada di objek, sehingga setiap objek memiliki dua metode ini. Terkadang kita perlu menerapkan kebutuhan spesifik dan mungkin harus menulis ulang kedua metode ini. Hari ini, kami akan memperkenalkan fungsi dari kedua metode ini.
Metode setara () dan hashcode () digunakan untuk membandingkan di kelas yang sama, terutama saat menyimpan objek kelas yang sama dalam wadah seperti diatur untuk menyimpan objek di kelas yang sama.
Di sini pertama -tama kita perlu memahami masalah:
Dua objek dengan equals () sama, hashCode () harus sama, dan dua objek dengan equals () tidak sama, tidak dapat membuktikan bahwa kode hash mereka tidak sama. Dengan kata lain, untuk dua objek yang metode setara () tidak sama, hashcode () mungkin sama. (Pemahaman saya disebabkan oleh konflik kode hash saat dihasilkan)
Di sini kode hash seperti indeks masing -masing karakter dalam kamus, dan sama () seperti membandingkan kata yang berbeda di bawah karakter yang sama dalam kamus. Sama seperti dalam kamus, mencari dua kata "diri" dan "spontan" di bawah kata "diri" dalam kamus, jika sama () digunakan untuk menentukan kesetaraan permintaan kata, itu adalah kata yang sama. Sebagai contoh, dua kata yang dibandingkan dengan sama () adalah "diri", maka nilai yang diperoleh dengan metode hashcode () harus sama pada saat ini; Jika metode Equals () membandingkan kata -kata "diri" dan "spontan", maka hasilnya adalah Anda tidak ingin menunggu, tetapi kedua kata ini milik kata "diri" dan ketika mencari indeks, yaitu, hashcode () adalah sama. Jika sama () membandingkan kata "diri" dan "mereka", maka hasilnya juga berbeda, dan hasil yang diperoleh dengan kode hashcode () juga berbeda pada saat ini.
Sebaliknya: HashCode () berbeda, dan setara () dapat diperkenalkan; HashCode () sama, sama () mungkin sama atau mungkin tidak sama. Di kelas objek, metode hashcode () adalah metode lokal, yang mengembalikan nilai alamat objek. Metode Equals () di kelas objek juga membandingkan nilai alamat dari dua objek. Jika sama () sama, itu berarti bahwa nilai alamat dari kedua objek juga sama, tentu saja kode hashcode () sama;
Pada saat yang sama, algoritma hash memberikan efisiensi tinggi untuk menemukan elemen
Jika Anda ingin mengetahui apakah suatu objek terkandung dalam koleksi, bagaimana Anda menulis kode program perkiraan?
Anda biasanya mengambil setiap elemen satu per satu untuk membandingkan dengan objek yang Anda cari. Ketika Anda menemukan bahwa hasil dari perbandingan metode yang sama antara suatu elemen dan objek yang Anda cari, berhenti mencari dan mengembalikan informasi positif. Jika tidak, kembalikan informasi negatif. Jika ada banyak elemen dalam koleksi, seperti 10.000 elemen dan tidak mengandung objek yang Anda cari, itu berarti bahwa program Anda perlu mengambil 10.000 elemen dari koleksi dan membandingkan satu per satu untuk mendapatkan kesimpulan.
Seseorang telah menemukan algoritma hash untuk meningkatkan efisiensi menemukan elemen dari satu set. Dengan cara ini, set dibagi menjadi beberapa area penyimpanan. Setiap objek dapat menghitung kode hash, dan kode hash dapat dikelompokkan (dihitung menggunakan fungsi hash yang berbeda). Setiap kelompok sesuai dengan area penyimpanan tertentu. Menurut hash suatu objek, ia dapat menentukan area mana objek yang harus disimpan. Hashset menggunakan algoritma hash untuk mengakses set objek. Secara internal menggunakan metode mengambil sisa dari angka tertentu n (fungsi hash ini adalah yang termudah) untuk mengelompokkan dan membagi kode hash. Kelas Objek mendefinisikan metode hashcode () untuk mengembalikan kode hash setiap objek Java. Saat mencari objek dari koleksi hashset, sistem Java pertama -tama memanggil metode hashcode () objek untuk mendapatkan tabel kode hash objek. , kemudian temukan area penyimpanan yang sesuai berdasarkan hashing, dan akhirnya dapatkan setiap elemen di area penyimpanan dan bandingkan dengan objek untuk metode yang sama; Dengan cara ini, Anda bisa mendapatkan kesimpulan tanpa melintasi semua elemen dalam koleksi. Dapat dilihat bahwa koleksi hashset memiliki kinerja pengambilan objek yang baik, tetapi efisiensi menyimpan objek dalam koleksi hashset relatif rendah, karena ketika menambahkan objek ke koleksi hashset, Anda harus terlebih dahulu menghitung kode hash objek dan menentukan lokasi penyimpanan objek dalam koleksi berdasarkan kode hash ini. Untuk memastikan bahwa objek instance dari suatu kelas dapat disimpan secara normal dalam hashset, hasil dari dua objek instance dari kelas ini juga harus sama ketika hasil dibandingkan dengan metode Equals () sama; yaitu, jika hasil OBJ1.Equals (OBJ2) benar, maka hasil dari ekspresi berikut juga harus benar:
obj1.hashcode () == obj2.hashcode ()
Dengan kata lain: Ketika kita menulis ulang metode yang setara dengan objek, kita harus menulis ulang metode kode hash -nya. Namun, jika kita tidak menulis ulang metode kode hash -nya, metode kode hash dalam objek selalu mengembalikan alamat hash suatu objek, dan alamat ini tidak pernah sama. Jadi, bahkan jika metode Equals ditulis ulang saat ini, tidak akan ada efek khusus, karena jika metode kode hash tidak ingin menunggu, itu tidak akan memanggil metode Equals untuk perbandingan, sehingga tidak ada artinya.
Jika metode hashcode () dari suatu kelas tidak mengikuti persyaratan di atas, maka ketika hasil perbandingan antara dua objek instance dari kelas ini sama dengan metode Equals (), mereka tidak boleh disimpan dalam set yang ditetapkan pada saat yang sama. Namun, jika mereka disimpan dalam set hashset, karena nilai pengembalian metode hashcode () mereka berbeda (nilai pengembalian metode kode hash dalam objek selalu berbeda), objek kedua dapat dimasukkan ke dalam area yang berbeda dari objek pertama menurut perhitungan kode hash, sehingga tidak dapat membandingkan metode Equals dengan objek pertama, mungkin disimpan dalam kumpulan HAS HAS. Metode hashcode () di kelas objek tidak dapat memenuhi persyaratan objek yang disimpan dalam hashset, karena nilai pengembaliannya dihitung dari alamat memori objek. Nilai hash yang dikembalikan oleh objek yang sama kapan saja selama program menjalankan selalu tidak berubah. Oleh karena itu, selama itu adalah dua objek instance yang berbeda, bahkan jika hasil perbandingan dari metode Equals mereka sama, nilai pengembalian metode hashcode default mereka berbeda.
Mari kita lihat contoh spesifik:
Objek RectObject: Paket com.weijia.demo; RectObject kelas publik {public int x; int y publik; public rectobject (int x, int y) {this.x = x; this.y = y; } @Override public int hashCode () {final int prime = 31; Hasil int = 1; Hasil = hasil prime * + x; Hasil = hasil prime * + y; hasil pengembalian; } @Override public boolean sama (objek obj) {if (this == obj) mengembalikan true; if (obj == null) mengembalikan false; if (getClass ()! = obj.getClass ()) mengembalikan false; final rectobject lain = (rectobject) obj; if (x! = Other.x) {return false; } if (y! = Other.y) {return false; } return true; }} Kami mengesampingkan kode hash dan sama dengan metode dalam objek kelas induk dan melihat bahwa dalam kode hashcode dan sama dengan metode, jika nilai x dan y dari dua objek rectobject sama, nilai kode hash mereka sama, dan sama dengan pengembalian true;
Ini kode uji:
paket com.weijia.demo; impor java.util.hashset; Demo kelas publik {public static void main (string [] args) {hashset <RectObject> set = hashset baru <RectObject> (); Rectobject r1 = rectobject baru (3,3); RectObject R2 = RectObject baru (5,5); Rectobject r3 = rectobject baru (3,3); set.add (r1); set.add (r2); set.add (r3); set.add (r1); System.out.println ("size:"+set.size ()); }} Kami menyimpan empat objek ke dalam hashset dan mencetak ukuran koleksi set. Apa hasilnya?
Hasil Menjalankan: Ukuran: 2
Mengapa 2? Ini sangat sederhana, karena kami menulis ulang metode kode hashcode dari kelas RectObject. Selama nilai atribut x dan y dari objek rectobject sama, maka nilai kode hash -nya juga sama. Jadi pertama -tama bandingkan nilai kode hash. Nilai atribut X dan Y dari objek R1 dan R2 tidak sama, sehingga kode hash mereka berbeda, sehingga objek R2 dapat dimasukkan, tetapi nilai atribut X dan Y dari objek R3 sama dengan nilai atribut objek R1, sehingga kode hash sama. Pada saat ini, kami membandingkan metode Equals R1 dan R3, karena nilai X dan Y dari keduanya sama, sehingga objek R1 dan R3 sama, sehingga R3 tidak dapat dimasukkan. Juga, menambahkan R1 pada akhirnya tidak ditambahkan, sehingga hanya ada satu dua objek, R1 dan R2 pada set set.
Selanjutnya, kami mengomentari metode HashCode dalam objek RectObject, yaitu, kami tidak mengganti metode kode hash dalam objek objek, dan jalankan kode:
Hasil Menjalankan: Ukuran: 3
Hasil ini juga sangat sederhana. Pertama, menilai kode hash objek R1 dan objek R2. Karena metode kode hash dalam objek mengembalikan hasil konversi dari alamat memori lokal objek, kode hash dari berbagai objek instan berbeda. Demikian pula, karena kode hash R3 dan R1 juga tidak setara, tetapi R1 == R1, jadi di set akhir, hanya ada tiga objek R1, R2, dan R3, sehingga ukurannya 3
Selanjutnya, kami berkomentar konten dari metode Equals dalam objek RectObject, langsung mengembalikan False, tanpa mengomentari metode kode hash, dan jalankan kode:
Hasil Menjalankan: Ukuran: 3
Hasil ini sedikit tidak terduga, mari kita analisis:
Pertama, objek R1 dan R2 membandingkan kode hashcode, yang tidak sama, sehingga R2 dimasukkan ke dalam set, dan kemudian melihat metode kode hashcode R3 dan membandingkan R1 dan R3, yang sama, dan kemudian membandingkan metode yang setara. Karena metode yang sama selalu mengembalikan false, R1 dan R3 juga tidak setara, dan tidak perlu menyebutkan R3 dan R2. Kode hash dari keduanya tidak sama, jadi R3 dimasukkan ke dalam set, dan kemudian melihat R4, dan membandingkan R1 dan R4 menemukan bahwa kode hashc adalah sama. Saat membandingkan metode yang sama, karena sama dengan mengembalikan False, R1 dan R4 tidak sama, R2 dan R4 yang sama juga tidak setara, dan R3 dan R4 juga tidak setara, sehingga R4 dapat ditempatkan dalam set set, sehingga hasilnya harus ukuran: 4, jadi mengapa 3?
Saat ini, kita perlu memeriksa kode sumber hashset. Berikut adalah kode sumber dari metode add di hashset:
/*** Menambahkan elemen yang ditentukan ke set ini jika belum ada. * Lebih formal, menambahkan elemen yang ditentukan <tt> e </tt> ke set ini jika * set ini tidak mengandung elemen <tt> e2 </tt> sedemikian rupa sehingga * <tt> (e == null? E2 == null: e.Equals (e2)) </tt>. * Jika set ini sudah berisi elemen, panggilan meninggalkan set * tidak berubah dan mengembalikan <tt> false </tt>. * * @param e elemen yang akan ditambahkan ke set ini * @return <tt> true </tt> Jika set ini belum mengandung * elemen */ public boolean add (e e) {return map.put (e, present) == null; } Di sini kita dapat melihat bahwa hashset sebenarnya diimplementasikan berdasarkan hashmap. Kami mengklik metode HashMap, kode sumbernya adalah sebagai berikut:
/*** mengaitkan nilai yang ditentukan dengan kunci yang ditentukan dalam peta ini. * Jika peta sebelumnya berisi pemetaan untuk kunci, nilai * lama diganti. * * Kunci @param Kunci yang dengannya nilai yang ditentukan akan dikaitkan * nilai @param yang akan dikaitkan dengan kunci yang ditentukan * @return nilai sebelumnya yang terkait dengan <tt> key </tt>, atau * <tt> null </tt> jika tidak ada pemetaan untuk <tt> key </tt>. *; int hash = hash (kunci); int i = indexfor (hash, table.length); untuk (entri <k, v> e = tabel [i]; e! = null; e = e.next) {objek k; if (e.hash == hash && ((k = e.key) == key || key.equals (k))) {v oldValue = E.Value; E.Value = nilai; E.RecordAccess (ini); Kembalikan OldValue; }} modcount ++; addentry (hash, kunci, nilai, i); kembali nol; } Mari kita lihat kondisi penilaian IF.
Pertama, kami menentukan apakah kode hash sama. Jika tidak sama, lewati secara langsung. Jika sama, maka bandingkan apakah kedua objek ini sama atau metode yang sama dari kedua objek ini. Karena dilakukan atau dioperasikan, selama seseorang benar, kami dapat menjelaskannya di sini. Faktanya, ukuran set di atas adalah 3, karena R1 terakhir tidak dimasukkan, dan diperkirakan bahwa R1 == R1 kembali benar, jadi itu tidak dimasukkan. Jadi ukuran set adalah 3. Jika kita mengatur metode kode hash untuk selalu mengembalikan false, set ini akan 4.
Akhirnya, mari kita lihat kebocoran memori yang disebabkan oleh kode hash: lihat kode:
paket com.weijia.demo; impor java.util.hashset; Demo kelas publik {public static void main (string [] args) {hashset <RectObject> set = hashset baru <RectObject> (); Rectobject r1 = rectobject baru (3,3); RectObject R2 = RectObject baru (5,5); Rectobject r3 = rectobject baru (3,3); set.add (r1); set.add (r2); set.add (r3); r3.y = 7; System.out.println ("Ukuran sebelum dihapus:"+set.size ()); set.remove (r3); System.out.println ("Ukuran setelah penghapusan:"+set.size ()); }} Hasil Menjalankan:
Ukuran sebelum dihapus: 3
Ukuran yang Dihapus: 3
Rush, saya menemukan masalah, dan itu adalah masalah besar. Kami menelepon Remove untuk menghapus objek R3, berpikir bahwa itu telah dihapus, tetapi sebenarnya itu tidak dihapus. Ini disebut memori kebocoran, yang merupakan objek yang tidak digunakan, tetapi masih ada dalam memori. Jadi setelah kami mengoperasikan ini berkali -kali, memori meledak. Lihatlah Kode Sumber Hapus:
/*** Menghapus elemen yang ditentukan dari set ini jika ada. * Lebih formal, menghapus elemen <tt> e </tt> sedemikian rupa sehingga * <tt> (o == null? E == null: o.equals (e)) </tt>, * Jika set ini berisi elemen tersebut. Mengembalikan <tt> true </tt> Jika * set ini berisi elemen (atau setara, jika set ini * diubah sebagai hasil dari panggilan). (Set ini tidak akan berisi elemen * setelah panggilan kembali.) * * @Param o Objek yang akan dihapus dari set ini, jika ada * @return <tt> true </tt> jika set berisi elemen yang ditentukan */ boolean publik hapus (objek o) {return map.remove (o) == hadir; } Kemudian lihat kode sumber metode hapus:
/*** Menghapus pemetaan untuk kunci yang ditentukan dari peta ini jika ada. * * Kunci @param yang pemetaannya akan dihapus dari peta * @return nilai sebelumnya yang terkait dengan <tt> key </tt>, atau * <tt> null </tt> jika tidak ada pemetaan untuk <tt> key </tt>. *; return (e == null? null: e.value); }
Mari kita lihat kode sumber metode removeEntryForKey:
/** * Menghapus dan mengembalikan entri yang terkait dengan kunci yang ditentukan * dalam hashmap. Mengembalikan nol jika hashmap tidak mengandung pemetaan * untuk kunci ini. */ Entri terakhir <K, V> RemoveStRyForKey (Kunci Objek) {int hash = (key == null)? 0: hash (kunci); int i = indexfor (hash, table.length); Entri <k, v> prev = tabel [i]; Entri <k, v> e = prev; while (e! = null) {entri <k, v> next = e.next; Objek k; if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k)))) {modcount ++; ukuran--; if (prev == e) tabel [i] = selanjutnya; else prev.next = next; E.Recordremoval (ini); mengembalikan e; } prev = e; e = selanjutnya; } return e; } Kami melihat bahwa ketika memanggil metode hapus, pertama -tama kami akan menggunakan nilai kode hashcode objek untuk menemukan objek dan kemudian menghapusnya. Masalah ini karena kami telah memodifikasi nilai atribut Y objek R3. Dan karena metode kode hash dari objek RectObject memiliki nilai y yang berpartisipasi dalam operasi, kode hash dari objek R3 telah berubah, sehingga R3 tidak ditemukan dalam metode Hapus, sehingga penghapusan gagal. Artinya, kode hash R3 telah berubah, tetapi lokasi yang disimpannya belum diperbarui dan masih berada di lokasi aslinya, jadi ketika kami menggunakan kode hash baru untuk menemukannya, kami pasti tidak akan menemukannya.
Faktanya, metode di atas sangat mudah diimplementasikan: seperti yang ditunjukkan pada gambar:
Tabel hash linear yang sangat sederhana, fungsi hash yang digunakan adalah mod, kode sumber adalah sebagai berikut:
/*** Mengembalikan indeks untuk kode hash h. */ indeks int statis untuk (int h, panjang int) {return h & (length-1); } Ini sebenarnya adalah operasi mod, tetapi operasi semacam ini lebih efisien daripada % operasi.
1,2,3,4,5 berarti hasil mod, dan setiap elemen sesuai dengan struktur daftar yang ditautkan. Oleh karena itu, jika Anda ingin menghapus entri <k, v>, pertama -tama Anda akan mendapatkan kode hash, sehingga mendapatkan simpul header dari daftar yang ditautkan, dan kemudian beralih melalui daftar yang ditautkan. Jika kode hash dan sama sama, hapus elemen ini.
Kebocoran memori di atas memberi tahu saya sebuah pesan: Jika kami berpartisipasi dalam operasi kode hash dari nilai atribut objek, kami tidak dapat memodifikasi nilai atributnya saat menghapusnya, jika tidak masalah serius akan terjadi.
Bahkan, kita juga dapat melihat metode hashcode dan sama dengan metode tipe objek yang sesuai dengan 8 tipe data dasar.
Di antara mereka, kode hash dari tipe dasar dalam 8 sangat mudah untuk secara langsung mengembalikan ukuran numerik mereka. Objek string adalah melalui metode perhitungan yang kompleks, tetapi metode perhitungan ini dapat memastikan bahwa jika nilai string ini sama, kode hash mereka akan sama. 8 tipe dasar metode yang sama adalah untuk secara langsung membandingkan nilai numerik, dan metode tipe string sama membandingkan nilai string.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.