建議用 IDEA打開源碼配合來看
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
實際調用putVal方法:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
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);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
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;
}
代碼有點長, 我們一點一點來看:
首先,定義了4個變量 (Node<K,V>[] tab; Node<K,V> p; int n, i;),先不管他是什么意思,然后把一個 HashMap 的一個成員變量賦值給 tab,
由于我們第一次調用put方法, 所以成員變量此時的值為null,為null會調用一個 resize()方法,我們來看一下這個方法做了些什么。
這里將擴容的閥值賦值給成員變量 threshold 方便之后存取,然后new一個剛剛計算出數量的Note數組,再將Note數組賦值給成員變量table,此table就是HashMap具體存儲數據的地方,接下來,oldTab依然為null,所以直接返回初始化好的Note數組-->>
note中存儲了key的hash值,鍵值對,以及對下一個note節點的引用,由此可見,這是一個單向鏈表的結構
我們put數據的時候,會有兩種情況,一是上面這種,數組下標節點沒有數據,第二種是有值,接下來我們看第二種情況
這里又會有三種情況:
- 拿當前Map中的Note節點的hash值與要put進去的key的hash判斷,相等的話,再比較Map中Note節點的key與傳進來的key地址是否一致,如果一致,則將值覆蓋,不一致就再繼續進行key的equals比較,為true就覆蓋,不然繼續判斷。
- 判斷當前節點是否是一個紅黑樹結構,如果是的話,將數據存儲到樹節點中。
-
如果都不滿足,則將數據添加到鏈表中。說的很簡單,我們繼續看一下代碼:
p = 當前數組位置的元素
e = 當前數組位置的元素的鏈表的下一個
先判斷當前節點鏈表是否有下一個,如果沒有,則初始化Note節點,加入到鏈表中,再判斷鏈表的長度,有沒有達到轉換樹結構的閥值,達到了則將鏈表轉換為樹結構。
如果當前節點鏈表有下一個,判斷節點數據的hash值與傳進來的hash是否相等,相等則判斷節點的key與傳進來的key的地址值比較和equals比較,滿足任何一條,就退出。不滿足就繼續迭代當前數組位置的鏈表,執行前面的邏輯。
最后把Map的Size加1, 判斷是否需要擴容。
resize() 方法中還有許多邏輯判斷,喜歡研究的自己去看吧,作者太菜,不愛看算法