HashMap是Java中廣泛使用的數(shù)據(jù)結(jié)構之一,它提供了高效的鍵值對存儲和檢索功能。本文將深入探討HashMap的底層原理,包括它的數(shù)據(jù)結(jié)構、哈希算法、碰撞解決方法以及工作原理。
數(shù)據(jù)結(jié)構
HashMap的底層數(shù)據(jù)結(jié)構是數(shù)組和鏈表(或紅黑樹)。數(shù)組用于存儲元素的桶(bucket),每個桶是一個鏈表或紅黑樹的頭節(jié)點。當出現(xiàn)大量元素存儲在同一個桶內(nèi)時,鏈表將轉(zhuǎn)換為紅黑樹,以提高檢索效率。
哈希算法
HashMap使用哈希算法將鍵映射到數(shù)組索引位置。哈希算法的關鍵是?hashCode()
?方法。當調(diào)用?hashCode()
?方法時,HashMap會根據(jù)鍵的特性生成一個哈希碼。哈希碼是一個整數(shù),它代表了鍵在數(shù)組中的位置。
碰撞解決方法
在哈希算法中,不同的鍵可能會生成相同的哈希碼,這就是所謂的碰撞(collision)。HashMap使用鏈表和紅黑樹來解決碰撞問題。
當元素被插入到HashMap中時,首先根據(jù)鍵的哈希碼計算出數(shù)組索引。如果該索引位置為空,則直接將元素插入其中。如果該索引位置已經(jīng)存在元素,則遍歷鏈表或紅黑樹,判斷鍵是否已經(jīng)存在。如果鍵已存在,則更新對應的值;如果鍵不存在,則將新元素添加到鏈表或紅黑樹的末尾或合適位置。
當鏈表的長度超過8個節(jié)點,并且數(shù)組的長度大于64時,鏈表將被轉(zhuǎn)換為紅黑樹。這是為了提高在大量元素存儲在同一個桶內(nèi)時的查找效率。
源碼
// 遍歷鏈表
for (int binCount = 0; ; ++binCount) {
// 遍歷到鏈表最后一個節(jié)點
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 如果鏈表元素個數(shù)大于等于TREEIFY_THRESHOLD(8)
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
// 紅黑樹轉(zhuǎn)換(并不會直接轉(zhuǎn)換成紅黑樹)
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
工作原理
當使用HashMap進行插入、獲取或刪除操作時,它會執(zhí)行以下步驟:
- 根據(jù)鍵的?
hashCode()
?方法計算哈希碼。 - 根據(jù)哈希碼找到數(shù)組索引位置。
- 如果該索引位置為空,直接插入元素。
- 如果該索引位置不為空,檢查鍵是否已存在,如果存在則更新值,否則將元素插入鏈表或紅黑樹。
- 如果鏈表長度過長,并且數(shù)組長度足夠大,將鏈表轉(zhuǎn)換為紅黑樹。
- 根據(jù)需要調(diào)整數(shù)組的大小(擴容或收縮)以保持較低的負載因子。
總結(jié)
HashMap是一種高效的鍵值對存儲和檢索數(shù)據(jù)結(jié)構。它的底層原理基于數(shù)組、鏈表和紅黑樹,使用哈希算法將鍵映射到數(shù)組索引位置。碰撞問題通過鏈表和紅黑樹的使用得以解決。了解HashMap的底層原理對于理解其運作方式、優(yōu)化性能以及避免潛在的問題非常重要。通過合理選擇哈希函數(shù)和調(diào)整負載因子,我們可以確保HashMap在各種場景下都能夠提供高效的數(shù)據(jù)存儲和檢索能力。