Sesuai pemahaman saya, saya pikir:
- Sangat sah untuk dua objek memiliki kode hash yang sama.
- Jika dua objek sama (menggunakan metode equals ()) maka mereka memiliki kode hash yang sama.
- Jika dua objek tidak sama maka mereka tidak dapat memiliki kode hash yang sama
Apakah saya benar?
Sekarang jika benar, saya memiliki pertanyaan berikut: Secara HashMap
internal menggunakan kode hash objek. Jadi jika dua objek dapat memiliki kode hash yang sama, lalu bagaimana cara HashMap
melacak kunci yang digunakannya?
Bisakah seseorang menjelaskan bagaimana HashMap
internal menggunakan kode hash objek?
java
hashmap
hashcode
hash-function
akshay
sumber
sumber
Jawaban:
Hashmap berfungsi seperti ini (ini sedikit disederhanakan, tetapi menggambarkan mekanisme dasarnya):
Ia memiliki sejumlah "ember" yang digunakannya untuk menyimpan pasangan nilai kunci. Setiap ember memiliki nomor unik - itulah yang mengidentifikasi ember. Saat Anda menempatkan pasangan nilai kunci ke dalam peta, peta hash akan melihat kode hash kunci, dan menyimpan pasangan dalam ember yang pengenalnya adalah kode hash kunci. Misalnya: Kode hash kunci adalah 235 -> pasangan disimpan dalam nomor ember 235. (Perhatikan bahwa satu ember dapat menyimpan lebih dari satu pasangan nilai kunci).
Saat Anda mencari nilai dalam hashmap, dengan memberinya kunci, pertama-tama ia akan melihat kode hash kunci yang Anda berikan. Hashmap kemudian akan melihat ke dalam ember yang sesuai, dan kemudian akan membandingkan kunci yang Anda berikan dengan kunci semua pasangan dalam ember, dengan membandingkannya dengan
equals()
.Sekarang Anda dapat melihat bagaimana ini sangat efisien untuk mencari pasangan kunci-nilai di peta: dengan kode hash kunci, hashmap segera tahu di mana ember harus dilihat, sehingga hanya perlu menguji terhadap apa yang ada di ember itu.
Melihat mekanisme di atas, Anda juga dapat melihat persyaratan apa yang diperlukan pada
hashCode()
danequals()
metode kunci:Jika dua kunci adalah sama (
equals()
kembalitrue
ketika Anda membandingkannya),hashCode()
metode mereka harus mengembalikan nomor yang sama. Jika kunci melanggar ini, maka kunci yang sama mungkin disimpan dalam ember yang berbeda, dan hashmap tidak akan dapat menemukan pasangan nilai kunci (karena akan terlihat di ember yang sama).Jika dua kunci berbeda, maka tidak masalah apakah kode hashnya sama atau tidak. Mereka akan disimpan dalam ember yang sama jika kode hash mereka sama, dan dalam kasus ini, hashmap akan digunakan
equals()
untuk membedakan mereka.sumber
hashCode()
metode mereka mengembalikan kode hash yang berbeda, maka metodeequals()
danhashCode()
kelas kunci melanggar kontrak dan Anda akan mendapatkan hasil yang aneh ketika menggunakan kunci-kunci itu di aHashMap
.HashMap
cari kode sumbernya , yang dapat Anda temukan di filesrc.zip
di direktori instalasi JDK Anda.Penegasan ketiga Anda tidak benar.
Sangat sah untuk dua objek yang tidak sama memiliki kode hash yang sama. Ini digunakan
HashMap
sebagai "filter akses pertama" sehingga peta dapat dengan cepat menemukan entri yang mungkin dengan kunci yang ditentukan. Kunci dengan kode hash yang sama kemudian diuji untuk kesetaraan dengan kunci yang ditentukan.Anda tidak akan ingin persyaratan bahwa dua benda yang tidak sama tidak bisa memiliki kode hash yang sama, karena kalau tidak itu akan membatasi Anda untuk 2 32 objek mungkin. (Ini juga berarti bahwa tipe yang berbeda bahkan tidak bisa menggunakan bidang objek untuk menghasilkan kode hash, karena kelas lain dapat menghasilkan hash yang sama.)
sumber
HashMap
adalah array dariEntry
objek.Pertimbangkan
HashMap
hanya sebagai array objek.Lihatlah apa ini
Object
:Setiap
Entry
objek mewakili pasangan nilai kunci. Bidangnext
merujuk keEntry
objek lain jika ember memiliki lebih dari satuEntry
.Terkadang mungkin terjadi bahwa kode hash untuk 2 objek berbeda adalah sama. Dalam hal ini, dua objek akan disimpan dalam satu ember dan akan disajikan sebagai daftar tertaut. Titik masuk adalah objek yang baru ditambahkan. Objek ini merujuk ke objek lain dengan
next
bidang dan sebagainya. Entri terakhir mengacu padanull
.Ketika Anda membuat
HashMap
dengan konstruktor defaultArray dibuat dengan ukuran 16 dan keseimbangan beban standar 0,75.
Menambahkan pasangan nilai kunci baru
hash % (arrayLength-1)
mana elemen harus ditempatkan (nomor ember)HashMap
, maka nilai akan ditimpa.Jika ember sudah memiliki setidaknya satu elemen, yang baru akan ditambahkan dan ditempatkan di posisi pertama dari ember. Bidangnya
next
mengacu pada elemen lama.Penghapusan
hash % (arrayLength-1)
Entry
. Jika elemen yang diinginkan tidak ditemukan, kembalinull
sumber
hash % (arrayLength-1)
itu akan menjadihash % arrayLength
. Tapi sebenarnya ituhash & (arrayLength-1)
. Yaitu, karena menggunakan kekuatan dua (2^n
) untuk panjang array, mengambiln
bit paling tidak signifikan.int
yang tentu saja bisa negatif, melakukan modulo pada angka negatif akan memberi Anda angka negatifAnda dapat menemukan informasi yang sangat baik di http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-java.html
Untuk meringkas:
HashMap bekerja berdasarkan prinsip hashing
put (key, value): HashMap menyimpan objek key dan value sebagai Map.Entry. Hashmap menerapkan kode hash (kunci) untuk mendapatkan ember. jika ada tabrakan, HashMap menggunakan LinkedList untuk menyimpan objek.
dapatkan (kunci): HashMap menggunakan kode hash Objek Kunci untuk mengetahui lokasi bucket dan kemudian memanggil metode keys.equals () untuk mengidentifikasi simpul yang benar di LinkedList dan mengembalikan objek nilai terkait untuk kunci tersebut di Java HashMap.
sumber
Berikut ini penjelasan kasar tentang
HashMap
mekanisme, untukJava 8
versi, (mungkin sedikit berbeda dengan Java 6) .Struktur data
hash Nilai hash dihitung melalui
hash()
tombol, dan itu menentukan ember hashtable mana yang akan digunakan untuk kunci yang diberikan.Saat jumlah elemen dalam ember kecil, daftar tertaut tunggal digunakan.
Ketika jumlah elemen dalam ember besar, pohon merah-hitam digunakan.
Kelas (internal)
Map.Entry
Mewakili satu entitas dalam peta, entitas kunci / nilai.
HashMap.Node
Versi simpul daftar tautan.
Itu bisa mewakili:
Karena memiliki properti hash.
HashMap.TreeNode
Versi pohon dari node.
Bidang (internal)
Node[] table
Tabel ember, (kepala daftar yang ditautkan).
Jika ember tidak mengandung elemen, maka itu nol, sehingga hanya mengambil ruang referensi.
Set<Map.Entry> entrySet
Set entitas.int size
Jumlah entitas.
float loadFactor
Tunjukkan seberapa penuh tabel hash diizinkan, sebelum mengubah ukuran.
int threshold
Ukuran selanjutnya untuk mengubah ukuran.
Rumus:
threshold = capacity * loadFactor
Metode (internal)
int hash(key)
Hitung hash dengan kunci.
Bagaimana cara memetakan hash ke bucket?
Gunakan logika berikut:
Tentang kapasitas
Dalam tabel hash, kapasitas berarti jumlah bucket, bisa didapat dari
table.length
.Juga dapat dihitung melalui
threshold
danloadFactor
, dengan demikian tidak perlu didefinisikan sebagai bidang kelas.Dapat memperoleh kapasitas efektif melalui:
capacity()
Operasi
Pertama temukan ember berdasarkan nilai hash, lalu lingkar daftar yang ditautkan atau cari pohon yang diurutkan.
Pertama menemukan ember sesuai dengan nilai kunci.
Kemudian coba cari nilainya:
Ketika
threshold
tercapai, akan menggandakan kapasitas hashtable (table.length
), kemudian melakukan hash ulang pada semua elemen untuk membangun kembali tabel.Ini bisa menjadi operasi yang mahal.
Performa
Kompleksitas waktu adalah
O(1)
, karena:O(1)
.O(1)
.O(1)
tidakO(log N)
.sumber
Kode hash menentukan ember mana yang harus diperiksa oleh hashmap. Jika ada lebih dari satu objek dalam bucket maka pencarian linier dilakukan untuk menemukan item mana dalam bucket yang sama dengan item yang diinginkan (menggunakan
equals()
metode).Dengan kata lain, jika Anda memiliki kode hash yang sempurna maka akses hashmap konstan, Anda tidak akan perlu mengulangi melalui ember (secara teknis Anda juga harus memiliki ember MAX_INT, implementasi Java dapat berbagi beberapa kode hash dalam ember yang sama untuk mengurangi persyaratan ruang). Jika Anda memiliki kode hash terburuk (selalu mengembalikan nomor yang sama) maka akses hashmap Anda menjadi linier karena Anda harus mencari melalui setiap item di peta (mereka semua dalam ember yang sama) untuk mendapatkan apa yang Anda inginkan.
Sebagian besar kode hash yang ditulis dengan baik tidak sempurna tetapi cukup unik untuk memberi Anda akses yang lebih atau kurang konstan.
sumber
Anda salah pada poin tiga. Dua entri dapat memiliki kode hash yang sama tetapi tidak sama. Lihatlah implementasi HashMap.get dari OpenJdk . Anda dapat melihat bahwa itu memeriksa bahwa hash sama dan kunci sama. Jika koma tiga benar, maka tidak perlu untuk memeriksa bahwa kunci sama. Kode hash dibandingkan sebelum kunci karena yang pertama adalah perbandingan yang lebih efisien.
Jika Anda tertarik untuk belajar lebih banyak tentang ini, lihat artikel Wikipedia tentang Open Collision Resolution Collision , yang saya percaya adalah mekanisme yang digunakan oleh implementasi OpenJdk. Mekanisme yang agak berbeda dari pendekatan "ember" salah satu jawaban lain menyebutkan.
sumber
Jadi di sini kita melihat bahwa jika kedua objek S1 dan S2 memiliki konten yang berbeda, maka kami cukup yakin bahwa metode Hashcode kami yang ditimpa akan menghasilkan Hashcode yang berbeda (116232.1111601) untuk kedua objek. SEKARANG karena ada kode hash yang berbeda, sehingga tidak akan repot memanggil metode EQUALS. Karena Hashcode yang berbeda MENJAMIN konten yang BERBEDA dalam suatu objek.
sumber
Pembaruan Java 8 di HashMap-
Anda melakukan operasi ini dalam kode Anda -
jadi, anggap kode hash Anda dikembalikan untuk kedua tombol
"old"
dan"very-old"
sama. Lalu apa yang akan terjadi.myHashMap
adalah HashMap, dan misalkan pada awalnya Anda tidak menentukan kapasitasnya. Jadi kapasitas default sesuai java adalah 16. Jadi sekarang segera setelah Anda menginisialisasi hashmap menggunakan kata kunci baru, itu menciptakan 16 ember. sekarang ketika Anda mengeksekusi statement- pertamakemudian kode hash
"old"
dihitung, dan karena kode hash bisa berupa bilangan bulat yang sangat besar juga, jadi, java melakukan ini secara internal - (hash adalah kode hash di sini dan >>> adalah pergeseran yang benar)jadi untuk memberikan gambar yang lebih besar, itu akan mengembalikan beberapa indeks, yang akan menjadi antara 0 hingga 15. Sekarang pasangan nilai kunci Anda
"old"
dan"old-value"
akan dikonversi ke kunci objek Entry dan variabel instance nilai. dan kemudian objek entri ini akan disimpan dalam ember, atau Anda dapat mengatakan bahwa pada indeks tertentu, objek entri ini akan disimpan.FYI- Entri adalah kelas di antarmuka Peta- Peta. Masukkan, dengan tanda tangan / definisi ini
sekarang ketika Anda menjalankan pernyataan berikutnya -
dan
"very-old"
memberikan kode hash yang sama dengan"old"
, sehingga pasangan nilai kunci baru ini dikirim lagi ke indeks yang sama atau ember yang sama. Tetapi karena bucket ini tidak kosong, makanext
variabel objek Entry digunakan untuk menyimpan pasangan nilai kunci baru ini.dan ini akan disimpan sebagai daftar tertaut untuk setiap objek yang memiliki kode hash yang sama, tetapi TRIEFY_THRESHOLD ditentukan dengan nilai 6. jadi setelah mencapai ini, daftar tertaut dikonversi ke pohon seimbang (pohon merah-hitam) dengan elemen pertama sebagai akar.
sumber
Setiap objek entri mewakili pasangan nilai kunci. Bidang selanjutnya merujuk ke objek Entri lainnya jika ember memiliki lebih dari 1 Entri.
Terkadang mungkin terjadi bahwa kode hash untuk 2 objek berbeda adalah sama. Dalam hal ini 2 objek akan disimpan dalam satu ember dan akan disajikan sebagai LinkedList. Titik masuk adalah objek yang baru ditambahkan. Objek ini merujuk ke objek lain dengan bidang selanjutnya dan seterusnya. Entri terakhir mengacu pada nol. Saat Anda membuat HashMap dengan konstruktor default
Array dibuat dengan ukuran 16 dan keseimbangan beban standar 0,75.
(Sumber)
sumber
Peta hash bekerja berdasarkan prinsip hashing
Metode HashMap get (Key k) memanggil metode hashCode pada objek kunci dan menerapkan hashValue yang dikembalikan ke fungsi hash statisnya sendiri untuk menemukan lokasi bucket (array backing) di mana kunci dan nilai disimpan dalam bentuk kelas bersarang yang disebut Entri (Peta. Masuk) . Jadi Anda telah menyimpulkan bahwa dari baris sebelumnya bahwa Kedua kunci dan nilai disimpan dalam ember sebagai bentuk objek Entri. Jadi berpikir bahwa nilai Hanya disimpan dalam ember tidak benar dan tidak akan memberikan kesan yang baik pada pewawancara.
Jika kunci nol, maka kunci kosong selalu memetakan ke hash 0, dengan demikian indeks 0.
Jika kunci bukan nol maka akan memanggil fungsi pada objek kunci, lihat baris 4 pada metode di atas yaitu key.hashCode (), jadi setelah key.hashCode () mengembalikan hashValue, baris 4 terlihat seperti
dan sekarang, ini berlaku hashValue dikembalikan ke fungsi hashing sendiri.
Kita mungkin bertanya-tanya mengapa kita menghitung nilai hash lagi menggunakan hash (hashValue). Jawabannya adalah itu membela terhadap fungsi hash kualitas buruk.
Sekarang nilai hash akhir digunakan untuk menemukan lokasi bucket tempat objek Entri disimpan. Objek entri disimpan dalam bucket seperti ini (hash, key, value, bucketindex)
sumber
Saya tidak akan masuk ke rincian tentang cara kerja HashMap, tetapi akan memberikan contoh sehingga kita bisa mengingat bagaimana HashMap bekerja dengan menghubungkannya dengan kenyataan.
Kami memiliki Key, Value, HashCode, dan bucket.
Untuk beberapa saat, kami akan menghubungkan mereka masing-masing dengan yang berikut:
Menggunakan Map.get (kunci):
Stevie ingin pergi ke rumah temannya (Josse) yang tinggal di sebuah vila di sebuah masyarakat VIP, biarlah itu JavaLovers Society. Alamat Josse adalah SSN-nya (yang berbeda untuk semua orang). Ada indeks yang dipertahankan di mana kami menemukan nama Society berdasarkan SSN. Indeks ini dapat dianggap sebagai algoritma untuk mengetahui HashCode.
Menggunakan Map.put (kunci, Nilai)
Ini menemukan masyarakat yang cocok untuk Nilai ini dengan menemukan HashCode dan kemudian nilainya disimpan.
Saya harap ini membantu dan ini terbuka untuk modifikasi.
sumber
Ini akan menjadi jawaban yang panjang, minum dan baca terus ...
Hashing adalah tentang menyimpan pasangan nilai kunci dalam memori yang dapat dibaca dan ditulis lebih cepat. Ini menyimpan kunci dalam array dan nilai-nilai dalam LinkedList.
Katakanlah saya ingin menyimpan 4 pasangan nilai kunci -
Jadi untuk menyimpan kunci, kita membutuhkan array 4 elemen. Sekarang bagaimana cara memetakan salah satu dari 4 kunci ini ke 4 indeks array (0,1,2,3)?
Jadi java menemukan kode hash kunci individu dan memetakannya ke indeks array tertentu. Rumus Hashcode adalah -
Hash and girl !! Saya tahu apa yang Anda pikirkan. Ketertarikan Anda tentang duet liar itu mungkin membuat Anda kehilangan hal penting.
Mengapa java mengalikannya dengan 31?
Sekarang bagaimana kode hash ini dipetakan ke indeks array?
jawabannya adalah
Hash Code % (Array length -1)
,. Jadi“girl”
dipetakan ke(3173020 % 3) = 1
dalam kasus kami. yang merupakan elemen kedua dari array.dan nilai "ahhan" disimpan dalam LinkedList yang terkait dengan indeks array 1.
HashCollision - Jika Anda mencoba menemukan
hasHCode
kunci“misused”
dan“horsemints”
menggunakan formula yang dijelaskan di atas Anda akan melihat keduanya memberi kami sama1069518484
. Whooaa !! pelajaran yang dipetik -Sekarang peta hash terlihat seperti -
Sekarang jika beberapa badan mencoba menemukan nilai untuk kunci
“horsemints”
, java dengan cepat akan menemukan kode hash itu, modul itu dan mulai mencari nilainya di LinkedList yang sesuaiindex 1
. Jadi dengan cara ini kita tidak perlu mencari semua indeks 4 array sehingga membuat akses data lebih cepat.Tapi, tunggu, sebentar. ada 3 nilai dalam tertaut terkait Array indeks 1, bagaimana ia mengetahui mana yang merupakan nilai untuk "horsemints" kunci?
Sebenarnya saya berbohong, ketika saya mengatakan HashMap hanya menyimpan nilai dalam LinkedList.
Ini menyimpan kedua pasangan nilai kunci sebagai entri peta. Jadi sebenarnya Map terlihat seperti ini.
Sekarang Anda dapat melihat Saat melintasi melalui LinkedList yang sesuai dengan ArrayIndex1, ia sebenarnya membandingkan kunci dari setiap entri dari LinkedList itu dengan “horsemints” dan ketika ia menemukannya, ia hanya mengembalikan nilai itu.
Semoga Anda bersenang-senang saat membacanya :)
sumber
Seperti yang dikatakan, gambar bernilai 1000 kata. Saya katakan: beberapa kode lebih baik dari 1000 kata. Berikut kode sumber HashMap. Dapatkan metode:
Jadi menjadi jelas bahwa hash digunakan untuk menemukan "ember" dan elemen pertama selalu diperiksa dalam ember itu. Jika tidak, maka
equals
kunci tersebut digunakan untuk menemukan elemen aktual dalam daftar tertaut.Mari kita lihat
put()
metodenya:Ini sedikit lebih rumit, tetapi menjadi jelas bahwa elemen baru diletakkan di tab pada posisi yang dihitung berdasarkan hash:
i = (n - 1) & hash
di sinii
adalah indeks di mana elemen baru akan diletakkan (atau itu adalah "ember").n
adalah ukurantab
array (array "ember").Pertama, dicoba untuk dimasukkan sebagai elemen pertama dalam "ember" itu. Jika sudah ada elemen, maka tambahkan node baru ke daftar.
sumber