1. hashMap如何計算key的hash值?爲什麼?
hash = (h=key.hashCode()) ^ (h >>> 16)
因爲在計算下標值的時候,採用的公式如下,其中n是table的長度。
index = (n-1) & hash
計算index的時候,都是取hash值的低n位,高位無法參與運算。爲了降低hash碰撞的概率,取高16位和低16位異或的結果作爲新的低16位,這樣低16位中融合了高16位的特徵,這又稱爲擾動函數。
2. 爲什麼hashMap的table長度都是2的n次冪?
- 和下標值的計算公式有關。方便進行&運算,提高效率,&比取模運算的效率高
index = (n-1) & hash
table的長度是2的n次冪,纔可以使用上面的公式計算下標值。因爲n-1的二進制的低n位是“低位掩碼”(都是1),使得hash的低n位都可以參與到index的計算中來。
舉例說明,假如讓n=14,那麼n-1(13)的二進制表示就是1101,index的末尾第二位永遠不會等於1。也就是說末尾第二位是1的index,永遠不會被命中。
- 和擴容過程中Node的遷移有關。令table長度是2的n次冪,則只需要觀察更高一位是0還是1。若是0,則新的下標值等於舊的下標值,若是1,則新的下標值等於舊的下標值加table舊的長度。
3. hashMap初始值如何給定?
預計要存入的數量 / 負載因子 + 1
4. hashMap 與 hashTable 有什麼區別?
- hashMap線程不安全,效率比較高;hashTable線程安全(syncronized),效率低
- hashMap的key和value都可以爲空,hashTable都不能爲空
5. hashMap線程安全嗎?爲什麼?
hashMap不是線程安全的。
推薦一篇好文,值得細細品。深入解讀HashMap線程安全性問題
文中大概提及了三個不安全的因素:
- 併發put導致數據丟失。
- resize的時候get到null。
- jdk1.7擴容時形成循環鏈表(1.8已經解決)
因爲1.7中鏈表採用頭插法,第一個線程中A->B, 第二個線程中把數據遷移到新的table,變成了B->A,之後第一個線程又會把A.next指向B,這樣就形成了循環鏈表。jdk1.8中採用尾插法,解決了此問題。