HashMap的JDK1.8的實現主要是由數組+單向鏈表+紅黑樹組成的,數據存儲結構如下圖。(JDK1.8之前採用的只是,數組+單向鏈表)
當鏈表長度大於8時會先判斷下,數組的長度是否小於64,如果小於則進行數組擴容;否則將鏈表結構轉換爲紅黑樹。
HashMap默認數組長度爲16,在往map中放元素時,首先會根據key進行Hash操作拿到hash值,然後進行取模操作,hash%16
得到在table中的index。當Key的hash值相同的時候,判斷key的equals方法,一樣替換原有的value,不一致並且鏈表長度小於8則將該元素放到原有元素的next節點中,並將數組中的元素替換成最新的節點。總的來說,如果該數組在該位置上已經存放了其他元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放入鏈尾.如果數組中該位置沒有元素,就直接將該元素放到數組的該位置上。
一 初始化
構造方法如上,前3個都只是對屬性進行初始化設值,第4個方法會將參數中的Entry進行put操作,調用方法:putMapEntries(m, false)
主要邏輯:1.判斷是否初始化,沒有進行初始化操作,設置數據的大小;2.判斷參數m的size是否大於數據的最大長度,大於進行擴容
3.將m中的entry放到map中;
二 主要方法刨析
put方法
public V put(K key, V value) {
//調用putVal()方法完成
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//判斷table是否初始化,否則初始化操作
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//計算存儲的索引位置,如果沒有元素,直接賦值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//節點若已經存在,執行賦值操作
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//判斷鏈表是否是紅黑樹
else if (p instanceof TreeNode)
//紅黑樹對象操作
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//爲鏈表,
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//鏈表長度8,將鏈表轉化爲紅黑樹存儲
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//key存在,直接覆蓋
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
//記錄修改次數
++modCount;
//判斷是否需要擴容
if (++size > threshold)
resize();
//空操作
afterNodeInsertion(evict);
return null;
}
get方法
獲取該hash值在數組中的位置通過(n-1)&hash 得到index值,取值時每次首先判斷是不是第一個node,並且在獲取第二個節點的時候判斷下是不是紅黑樹類型的,是紅黑樹類型的通過getTreeNode方法去獲取value值,不然循環遍歷鏈表equals(k)進行判斷獲取。
補充:在位運算中hash%n 和 (n-1)&hash的值是相等的,所以在HashMap中用 hash%n來獲取數組下標,然後通過(n-1)&hash重新獲得下標值,但是爲什麼不再次調用hash%n來再次獲取呢。。?
參考:
原文鏈接:https://blog.csdn.net/qq_41345773/article/details/92066554