Java的HashMap
HashMap
HashMap原理?
Hash是一個用於存儲key-value鍵值對的集合,每個鍵值對也叫Entry,這些Entry分散存儲在一個數組當中,每個元素初始值都是Null,常用方法有put,get
put原理?
put(1,"A")
1)計算數組下標index=Hash(1)=hashcode(1)&(capacity-1)
2)插入數組talbe[index].value="A"
3)如果table[index]這個位置已經有值了,用鏈表來解決(Java8改用紅黑樹實現了)
table[index]->next.value="A"
get原理?
get(1, "A")
1)計算數組下標index=Hash(1)
2)從數組中獲取數據table[index]
3)如果這個index存在沖突,需要遍歷該處鏈表,比對key
HashMap初始長度?
默認初始長度是16,每次自動擴展或是手動初始化時必須是2的冪。為什麽呢?
根據key計算數組下標用到一個Hash函數,要保證通過Hash函數得到的數組下標均勻分布,這樣HashMap在put,get時才會更高效。計算數組下標的公式是:
index = hashcode(key) & (capacity-1) # capacity是hashmap中數組的長度,假設使用默認長度 key hashcode(key) capacity-1 index 1 49(?0011 0001?) 1111 1 a 97(110 0001) 1111 1 book 3029737(?00101110001110101110 1001?) 1111 9 apple 93029210(?010110001011100000110101 1010?) 1111 10 # 保證HashMap數組長度是2的冪可保證capacity-1的二進制全是1, # 如果hash函數是均勻的話,得到的index也是均勻的
如何擴容?
1)判斷是否擴容
# 滿足下列條件就需要擴容了
HashMap.Size >= Capacity * LoadFactor
# HashMap.Size表示HashMap中含有的元素個數
# Capacity表示HashMap中數組的長度
# LoadFactor表示負載因子,默認值為0.75f
2)Resize
創建一個空數組,長度為原數組的2倍。
Capacity = 2 * Capaicty
3)Rehash
數組長度變了,所以每個元素的數組下標也有可能會變,所以需要重新計算並添加到新的數組中。
非線程安全
並發下的Rehash可能產生條件競爭導致環形鏈接。具體分析看參考鏈接。
看參考文章中的分析能說出來,但是自己寫還是寫不出來。
與Java8的HashMap有什麽不同
存在哈希沖突的情況,比如兩個哈希值取模後落在同一個index,或者兩條不同的key有相同的哈希值。
JDK7的做法是建一條鏈表,後插入的元素在上面,一個個地執行上面的判斷。
而JDK8則在鏈表長度達到8,而且桶數量達到64時,建一棵紅黑樹,解決嚴重沖突時的性能問題。
參考
疫苗:JAVA HASHMAP的死循環 - 酷殼
漫畫:什麽是HashMap?
漫畫:高並發下的HashMap
HashMap完全解讀-HollisChuang‘s Blog
Java HashMap工作原理及實現 | Yikun
HashMap的工作原理 - ImportNew
Java的HashMap