HASHMAP(JDK1.7)最詳細原理分析(二)

昨天的文章裡我解釋了HASHMAP(JDK1.7)在PUT的時候會發生衝突,而解決衝突的方式就是使用鏈表,那麼我們假設HASHMAP存儲結構如下圖:

HASHMAP(JDK1.7)最詳細原理分析(二)

那麼節點1和節點2組成了一個鏈表,那麼現在如果再來PUT一個節點3,假設節點3也需要插在這個鏈表中,我們考慮鏈表的插入效率,將節點3插在鏈表的頭部是最快的,那麼就會如下圖:

HASHMAP(JDK1.7)最詳細原理分析(二)

那麼按照上圖這種插入辦法,會出現一個問題:

  • 當我們需要get(節點2)時,我們先將節點2的key進行哈希然後算出下標,拿到下標後可以定位到數組中的節點1,但是發現節點1不等於節點2,所以不是最終的結果,但是節點1存在下一個節點,所以可以順著向下的指針找到節點2。
  • 那麼當我們需要get(節點3)時呢,我們發現是找不到節點3的,所以當我們把節點簡單的插在鏈表的頭部是不行的。

那HashMap是怎麼實現的呢?HashMap確實是將節點插在鏈表的頭部,但是在插完之後HashMap會將整個鏈表向下移動一位,移動完之後就會變成:

HASHMAP(JDK1.7)最詳細原理分析(二)

那麼現在PUT的時候插入一個元素的思路就是:將新節點插在鏈表的頭部,此時新節點就是當前這個鏈表的頭節點,接下來把頭節點移動到數組位置即可。


當我們在使用HashMap的時候,還可能會出現下面的使用方式:


<code>HashMap<string> hashMap = new HashMap<>();hashMap.put("1", "2");String value = hashMap.put("1", "3");System.out.println(value);/<string>/<code>

第三行代碼也是PUT,而這個時候在HashMap裡會將value覆蓋,也就是key="1"對應的value最終為"3",而第三行代碼返回的value將會是2。

我們現在來考慮這個PUT它是如何實現的,其實很簡單,第三行代碼的邏輯也是先對"1"計算哈希值以及對應的數組下標,有了數組下標之後就可以找到對應的位置的鏈表,而在將新節點插入到鏈表之前,還需要判斷一下當前新節點的key值是不是已經在這個鏈表上存在,所以需要先去遍歷當前這個位置的鏈表,在遍歷的過程中如果找到了相同的key則會進行value的覆蓋,並且返回oldvalue。


好,寫到這裡其實對於HashMap的PUT的主要邏輯也差不多了,總結一下:

  1. PUT(key,value)
  2. int hashcode = key.hashCode();
  3. int index = hashcode & (數組長度-1)
  4. 遍歷index位置的鏈表,如果存在相同的key,則進行value覆蓋,並且返回之前的value值
  5. 將key,value封裝為節點對象(Entry)
  6. 將節點插在index位置上的鏈表的頭部
  7. 將鏈表頭節點移動到數組上

這是最核心的7步,然後在這個過程中還有很重要的一步就是擴容,而擴容是發生在插入節點之前的,也就是步驟4和5之間的。

那麼關於JDK1.7裡HashMap的擴容時會出現“死鎖”問題的,我們下篇文章繼續。


相信大家不喜歡在小小的手機屏幕上還看到一大塊的代碼,閱讀體驗不好,所以我寫作的風格會文字偏多一點。如果覺得有所收穫還希望多多轉發,讓更多人看到


分享到:


相關文章: