HashMap原始碼分析(二):看完徹底瞭解HashMap
上文講到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的實現。
複製操作是呼叫的transfer方法
在1.7中的resize結合一下我們的小例子可以這樣理解,去超市買一個大一點的水壺,然後把以前水壺裡面的水給倒進新的水壺裡面。再把我們當前的水壺的容量替換掉,告訴別人我的容量更大了。(強行比喻哈哈哈哈哈)
1.7中的resize就是這麼簡單,那我們在看一下1.8中的resize(),這樣再看就不會一臉懵逼了
我在這裡把1.8的resize方法分為兩部分
- 1.計算新的newCap(新的容量)和newThr(新閥值點)
- 2.複製新的陣列
第一部分
第二部分
對比一下1.7
- 1.7元素不需要更換位置。1.8元素的位置要麼是在原位置,要麼是在原位置再移動2次冪的位置
- 不需要像1.7一樣重新計算hash
2.刪除
刪除的話就是首先先找到元素的位置,如果是連結串列就遍歷連結串列找到元素之後刪除。如果是用紅黑樹就遍歷樹然後找到之後做刪除,樹小於6的時候要轉連結串列。
刪除方法:
呼叫removeNode:
3.查詢元素
查詢方法,通過元素的Key找到Value。
呼叫getNode()方法
看完可以知道邏輯是先通過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遍歷的資料不是有序的是無序