上文講到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遍歷的數據不是有序的是無序的