昨天的文章裡我解釋了HASHMAP(JDK1.7)在PUT的時候會發生衝突,而解決衝突的方式就是使用鏈表,那麼我們假設HASHMAP存儲結構如下圖:
那麼節點1和節點2組成了一個鏈表,那麼現在如果再來PUT一個節點3,假設節點3也需要插在這個鏈表中,我們考慮鏈表的插入效率,將節點3插在鏈表的頭部是最快的,那麼就會如下圖:
那麼按照上圖這種插入辦法,會出現一個問題:
- 當我們需要get(節點2)時,我們先將節點2的key進行哈希然後算出下標,拿到下標後可以定位到數組中的節點1,但是發現節點1不等於節點2,所以不是最終的結果,但是節點1存在下一個節點,所以可以順著向下的指針找到節點2。
- 那麼當我們需要get(節點3)時呢,我們發現是找不到節點3的,所以當我們把節點簡單的插在鏈表的頭部是不行的。
那HashMap是怎麼實現的呢?HashMap確實是將節點插在鏈表的頭部,但是在插完之後HashMap會將整個鏈表向下移動一位,移動完之後就會變成:
那麼現在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的主要邏輯也差不多了,總結一下:
- PUT(key,value)
- int hashcode = key.hashCode();
- int index = hashcode & (數組長度-1)
- 遍歷index位置的鏈表,如果存在相同的key,則進行value覆蓋,並且返回之前的value值
- 將key,value封裝為節點對象(Entry)
- 將節點插在index位置上的鏈表的頭部
- 將鏈表頭節點移動到數組上
這是最核心的7步,然後在這個過程中還有很重要的一步就是擴容,而擴容是發生在插入節點之前的,也就是步驟4和5之間的。
那麼關於JDK1.7裡HashMap的擴容時會出現“死鎖”問題的,我們下篇文章繼續。
相信大家不喜歡在小小的手機屏幕上還看到一大塊的代碼,閱讀體驗不好,所以我寫作的風格會文字偏多一點。如果覺得有所收穫還希望多多轉發,讓更多人看到
閱讀更多 JAVA大神周瑜 的文章