HashMap的幾個要點:
1、數據存儲的底層數據結構
2、擴容機制 與rehash
3、同步問題(HashMap HashTable ConcurrentHashMap的區別與理解)
一、數據結構與存儲
1、HashMap底層是通過數組+鏈表的數據結構實現的。
2、整體來看hashMap中所有數據都存於一個數組table中,數組中的每個元素又是一個鏈表(hash值相同的元素會被放到一個鏈表中),鏈表中的每個節點存入的是每次put進去的元素(put進去的元素會被包裝成Node節點)。
3、在put元素obj時,會根據元素obj的key的hash值,計算其應該被放在數組哪個位置,即數組的下標i(計算方法,i=hash值%數組的長度,算出的餘數就是下標i)
3.1、計算出下標i後,看數組中table[i]是爲空,若爲空,則直接讓將obj包裝成一個Node節點,賦值給table[i](table[i]=new Node(obj) 代碼僅作示意);不爲空則,則依次比較table[i]這個鏈表每個元素的val,若相等,則覆蓋。若都不相等,則放到鏈表的最後。
4、爲了解決極端情況的hash衝突,jdk1.8引入了紅黑樹數據結構。當table[i]中的鏈表節點數大於8時,會將鏈表轉成紅黑樹進行存儲。當節點數小於6時,則會轉成鏈表儲存。
擴展閱讀 https://blog.csdn.net/Dr_Joker/article/details/57101211
二、擴容機制 與rehash
擴容機制。hashMap中有全負載因子爲0.75。當HashMap的size大於等於 table.length*0.75時,會進行擴容。將重新創建一個newTable來進行存儲。newTable的length爲table.length*2; 擴容後需要進行rehash。
rehash
1、新建一個hash桶newTable,size是原hash桶oldTable的兩倍。
2、遍歷oldTable中的每個元素,重新計算其hash值,並將其放入到newTable中
2、1 遍歷oldTable中的每個元素,(這裏的每個元素是鏈表)
2、1、1 將鏈表中的頭節點取出,暫存於e中,並將該鏈表在oldTable中的位置置空。
2、1、2 取出e.next元素,暫存於next中
2、1、3 對e進行計算,算出其在newTable中的位置的下標。
2、1、4 計算出下標後,根據下標,將e元素放入到newTable中(newTable的每個元素是鏈表,是將e放入到鏈表的頭節點)
2、1、4 將next賦值於e,進行while循環。
擴展閱讀 https://blog.csdn.net/longwoniu/article/details/48781777
三、同步問題
HashMap是線程不安全的。解決線程不安全的問題有三:
1、HashTable
2、Collections. synchronizedMap(Map)
3、ConcurrentHashMap
1和2都是有Map的每個方法上加上了synchronized關鍵字,以此來保證線程安全
3 創新點在於對Map進行分段加鎖,分成16段分別加鎖,所以支持16個線程同時進行寫操作。在讀的時候不受限制。