HashMap原始碼分析(二):看完徹底瞭解HashMap

亂敲程式碼發表於2019-07-18

上文講到HashMap的增加方法,現在繼續 上文連結

HashMap在上一篇原始碼分析的文章中,如果使用put的時候如果元素數量超過threshold就會呼叫resize進行擴容

1.擴容機制

想要了解HashMap的擴容機制你要有這兩個問題

  • 1.什麼時候才需要擴容
  • 2.HashMap的擴容是什麼

在新增元素的時候如果超過threshold設定的閥值點就會進行擴容,簡單的來說就是一個水壺容量是二升,然後這個時候已經滿了但是你還要繼續加水,咋辦?換個大的。所以HashMap的擴容就和你這個水壺一樣,水已經滿了那我就在換個大的水壺繼續加水。不過在你換水壺的時候是有很多條件的。

在我看這個resize的原始碼的時候我也是一臉懵逼,最後請教了大佬得到的回答是因為1.8加入了紅黑樹比較麻煩可以看一下1.7的,然後我有去網上看了一下別人寫的文章基本上都是基於1.7的resize。所以這裡就看1.7的resize來分析。

來看JDK1.7中resize的實現。

HashMap原始碼分析(二):看完徹底瞭解HashMap

複製操作是呼叫的transfer方法

HashMap原始碼分析(二):看完徹底瞭解HashMap

在1.7中的resize結合一下我們的小例子可以這樣理解,去超市買一個大一點的水壺,然後把以前水壺裡面的水給倒進新的水壺裡面。再把我們當前的水壺的容量替換掉,告訴別人我的容量更大了。(強行比喻哈哈哈哈哈)

1.7中的resize就是這麼簡單,那我們在看一下1.8中的resize(),這樣再看就不會一臉懵逼了

我在這裡把1.8的resize方法分為兩部分

  • 1.計算新的newCap(新的容量)和newThr(新閥值點)
  • 2.複製新的陣列

第一部分

HashMap原始碼分析(二):看完徹底瞭解HashMap

第二部分

HashMap原始碼分析(二):看完徹底瞭解HashMap

對比一下1.7

  • 1.7元素不需要更換位置。1.8元素的位置要麼是在原位置,要麼是在原位置再移動2次冪的位置
  • 不需要像1.7一樣重新計算hash

2.刪除

刪除的話就是首先先找到元素的位置,如果是連結串列就遍歷連結串列找到元素之後刪除。如果是用紅黑樹就遍歷樹然後找到之後做刪除,樹小於6的時候要轉連結串列。

刪除方法:

HashMap原始碼分析(二):看完徹底瞭解HashMap

呼叫removeNode:

HashMap原始碼分析(二):看完徹底瞭解HashMap

3.查詢元素

查詢方法,通過元素的Key找到Value。

HashMap原始碼分析(二):看完徹底瞭解HashMap

呼叫getNode()方法

HashMap原始碼分析(二):看完徹底瞭解HashMap

看完可以知道邏輯是先通過Key計算出索引的位置,然後先檢查第一個節點看看是否是我們要的節點,如果不是在去檢視是否死紅黑樹和連結串列。

4.遍歷

我們通過下面幾個例子來演示一下HashMap怎麼遍歷

1.分別遍歷Key和Values

        for (String key:map.keySet()){
            System.out.println(key);
        }
        for (Object value : map.values()) {
            System.out.println(value);
        }

2.迭代

        Iterator<Map.Entry<String, Object>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> mapEntry = iterator.next();
            System.out.println(mapEntry.getKey() + "====" + mapEntry.getValue());
        }

3.獲取 key 集合

    Set<String> keySet = map.keySet();
        for (String str : keySet) {
            System.out.println(str + "====" + map.get(str));
        }

4.獲取Entry 集合,遍歷Entry 集合

    Set<Map.Entry<String, Object>> entrySet = map.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            System.out.println(entry.getKey() + "====" + entry.getValue());
        }

對比來說使用迭代的方式是最好的,也可以在迭代的時候對集合的元素進行刪除

總結

基於JDK1.8的HashMap是由陣列+連結串列+紅黑樹組成,當連結串列長度超過 8 時會自動轉換成紅黑樹,當紅黑樹節點個數小於 6 時,又會轉化成連結串列。相對於早期版本的 JDK HashMap 實現,新增了紅黑樹作為底層資料結構,在資料量較大且雜湊碰撞較多時,能夠極大的增加檢索的效率。HashMap並不是執行緒安全的,支援K和V為null ,k重複會覆蓋,V可以重複,還有一點HashMap遍歷的資料不是有序的是無序的

相關文章