工作中常常會遇到常用的類,但是由於封裝的太好,一般也不會出現太多的問題,就導致對底層的實現了解的比較少,最近想把這些東西全部都梳理一下,也順便多學習一些實現思路。歡迎共同**帶著幾個問題去讀原始碼:
1. hashmap是基於哪種資料結構實現的?
2. hashmap是如何儲存的?
3. hashmap是如何取值的?
邊讀**邊理解:
其實只要閱讀他的put方法就能夠知道前兩個問題的答案!
public v put(k key, v value)
// 具體的實現方法
final v putval(int hash, k key, v value, boolean onlyifabsent,
boolean evict)
//如果找到了指定資料,則結束
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
// 如果沒有找到,則將e賦值給b繼續查詢他的下一級節點
p = e;}}
// e 物件如果不為空的話
// 將老值取出來
v oldvalue = e.value;
// 將新值替換進去
if (!onlyifabsent || oldvalue == null)
e.value = value;
afternodeaccess(e);
// 然後返回老值
return oldvalue;}}
//總是大小 + 1
++modcount;
// 如果大小超過設定的大小則進行重新調整
if (++size > threshold)
resize();
// 這個方法應該是交給你拓展的後置方法處理
afternodeinsertion(evict);
return
null;
}
resize()方法 - 乙個調整hashmap大小的方法,這個方法很關鍵,為後面的優化做了一些策略
final node resize()
// 如果小於1e並且值又大於預設的閥值大小時
else
if ((newcap = oldcap << 1) < maximum_capacity &&
oldcap >= default_initial_capacity)
// **將當前大小調整為兩倍大小**
newthr = oldthr << 1; // double threshold
}else
if (oldthr > 0) // initial capacity was placed in threshold
newcap = oldthr;
else
// 計算新的resize上限
if (newthr == 0)
//調整閥值大小
threshold = newthr;
@suppresswarnings()
// 重新構建乙個新的陣列鍊錶,並將大小調整至閥值的大小
node newtab = (node)new node[newcap];
table = newtab;
// 老的hashmap不為空的情況下
if (oldtab != null)
// 這裡用來處理非原索引的邏輯,和上面差不多
else
} // 迴圈遍歷鍊錶的下一級節點!!!!
while ((e = next) != null);
// 原索引還是存放到原來的位置
if (lotail != null)
// 非原索引則根據當作陣列位置+老的極限值大小的位置,相加,得到儲存的位置
if (hitail != null) }}
}}
return newtab;
}
梳理一下上面所了解到的知識:1. hashmap是由陣列+鍊錶+紅黑樹組成
2. hashmap的初始化陣列大小是16,儲存閥值大小當前陣列大小的75%,當陣列中的實際大小大於這個閥值是開始重新調整陣列大小,調整方案是以2倍遞增,當大小進行到integer.max_value,將不在擴容。
3. 擴容的方式是重新構建乙個新的鍊錶陣列,將老的陣列進行重組放入新的陣列鍊錶中
4. hashmap優化點:
1. 當鍊表長度超過8時,則將改造成treemap,也就是常說的紅黑樹,這樣做的目的就是為了防止某乙個鍊錶非常長,查詢速度很慢.
2. 擴容時會將鍊錶進行遍歷重組,重組的規則是判斷它的最後乙個bit是0還是1,因為我們使用的是2次冪的擴充套件(指長度擴為原來2倍),所以元素的位置要麼是在原位置,要麼是在原位置再移動2次冪的位置,所以打個比方,當前key處於陣列大小16的索引15位置,經過擴容之後的位置為15+16=31的位置。
3. 定位陣列的位置一共採用了三步:取key的hashcode、高位運算、取模運算
方法一:
static
final
int hash(object key)
方法二:
static
int indexfor(int h, int length)
你如果了解了上面如何定位陣列位置的話,應該就能夠有大概的思路,還是上遍**吧.
// 第乙個值是key的hash值
final nodegetnode(int hash, object key) while ((e = e.next) != null);}}
return
null;
}
參考資料: -寫的真他媽不是一般的好 java基礎之HashMap相關知識點
1.hashmap是一種由陣列和鍊錶構成的資料結構,用於儲存 key value對 元素,同時繼承了陣列的查詢優點和鍊錶的修改優點。2.hashmap是非同步的,所以速度很快。並且鍵和值可以為null。3.hashmap使用put key,value 方法儲存物件到hashmap中,使用get ke...
java 集合學習之hashMap
1 hashmap類繼承關係 public class hashmapextends abstractmap implements map,cloneable,serializable 存放示意圖 由此可以看出hash值一樣的節點會被存放在同一條鍊錶上,比原始遍歷equals查詢效率高 hash值相...
java之HashMap和HashSet的遍歷方法
今天去面試,面試官問到這個問題,發現自己用了這麼久的hashmap和hashset,竟然只勉強想到了一種方法,總結一下 hashmap遍歷方法 1 使用entryset for map.entryentry map.entryset 2 使用entry的迭代器 iterator iterator m...