HashMap內部原理詳解


1、什麼是HashMap?
答:HashMap 根據hash實現的一種Map接口實現類。
2、HashMap的底層數據結構?
答:在1.8之前,HashMap的數據結構:數組+鏈表
   在1.8之後,HashMap的數據結構:數組+鏈表+紅黑樹
3、HashMap的初始值?
答:capacity:代表table.length loadfactor:加載因子 threhold:擴容的門檻值
1)、DEFAULT_INITIAL_CAPACITY = 1<<4(初始容量)
2)、MAXIMUM_CAPACITY = 1<<30(最大容量)
3)、DEFAULT_LOAD_FACTOR = 0.75f(加載因子)
4)、TREEIFY_THRESHOLD = 8(鏈表轉化爲樹門檻值)
5)、UNTREEIFY_THRESHOLD = 6(樹轉化爲鏈表門檻值)
7)、loadFactor(加載因子)
8)、threshold(擴容的門檻值)
9)、size(map當前key-value的數目)
4、初始化HashMap?
答:
1)HashMap(int initialCapacity, float loadFactor) 
約束條件:0<=initialCapacity<=MAXIMUM_CAPACITY, loadFactor>=0, threshold=tableSizeFor(initialCapacity)(tableSizeFor(initialCapacity)大於等於initialCapacity的2的最小整數次冪)
2)HashMap(int initialCapacity) => HashMap(initialCapacity, DEFAULT_LOAD_FACTOR)
約束條件:同上
3) HashMap() 
約束條件:loadFactor = DEFAULT_LOAD_FACTOR
4)HashMap(Map<? extends K, ? extends V> m)
約束條件:loadFactor = DEFAULT_LOAD_FACTOR
5、put元素過程?
答:put<key, value> 允許空值
1)、通過hash函數,計算key的hash
2)、如果桶表爲空,通過resize函數初始化桶表
3)、計算hash值在表中的位置:(table.length - 1) & hash, 並查看在桶表中該位置是否有值,若無,則直接在該位置創建新值 new Node<>(hash, key, value, null)
4)、若該位置有值則判斷該值是否爲TreeNode
5)、若不是TreeNode,則從頭遍歷鏈表,若 Node.key == key || key.equals(Node.key) 則對值進行替換,否則,在尾部進行插入,校驗該桶的鏈表長度是否大於TREEIFY_THRESHOLD-1,若大於等於,則轉化爲樹結構。
6)、若是ThreeNode,則插入紅黑樹中
7)、若size大於threshold,則通過resize進行擴容
6、get元素過程?
答:get<key> 允許空值
1)、通過hash函數,計算key的hash
2)、計算hash值在表中的位置:(table.length - 1) & hash, 並查看在桶表中該位置是否有值,若無,則返回空
3)、若該位置有值則判斷該值是否爲TreeNode
4)、若不是TreeNode,則從頭遍歷鏈表若 Node.key == key || key.equals(Node.key) 則返回,若查詢不到,則返空
5)、若是ThreeNode,則從樹中獲取該值
7、remove元素過程?
答:remove<key>允許空值
1)、通過hash函數,計算key的hash
2)、假設桶表不爲空,計算hash值在表中的位置:(table.length - 1) & hash, 並查看在桶表中該位置是否有值,若無返回空
3)、若該位置有值則判斷該值是否爲TreeNode
4)、若不是TreeNode,則從頭遍歷鏈表,若 Node.key == key || key.equals(Node.key) 則對值進行刪除
5)、若是ThreeNode,則刪除該值
NOTE:
1)、hash函數:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
2)、resize函數:
創建桶表:
(1)、沒有初始化initialCapacity、loadFactor,則使用默認值初始化。
(2)、有初始值,則使用初始值初始化
(3)、創建桶表
擴容:
(1):capacity<<1且threhold<<1
(2):創建新桶表
(3):若節點爲TreeNode,則通過TreeNode方式進行rehash
(4):若節點爲Node,則重新進行rehash(注意:這裏面用了一個技巧(e.hash & oldCap) == 0的還是放在原來的位置,其他的放在原來位置+oldCap,(e.hash & oldCap) == 0表示小於原來容量的,這個等式基於oldCap一定是2的整數次冪,並且事實上也是這樣的。基於此舉個例子,假設之前容量爲16,則二級製表示爲00010000,那麼小於16的進行&操作,結果都爲零)
死鎖:java8的實現並不會發生死鎖,因爲死鎖的出現主要是順序不一致造成的。而java8的實現順序是一致的。

do {
    next = e.next;
    if ((e.hash & oldCap) == 0) {
        if (loTail == null)
            loHead = e;
        else
            loTail.next = e;
        loTail = e;
    }
    else {
        if (hiTail == null)
            hiHead = e;
        else
            hiTail.next = e;
        hiTail = e;
    }
} while ((e = next) != null);
if (loTail != null) {
    loTail.next = null;
    newTab[j] = loHead;
}
if (hiTail != null) {
    hiTail.next = null;
    newTab[j + oldCap] = hiHead;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章