Java HashMap的put方法
阿新 • • 發佈:2019-02-11
實習半年,基本上在寫scala和python。很少碰Java了,趁最近閒,補一波Java集合的東西。這篇主要分析HashMap的插入方法。之後再抽時間看下紅黑樹。
可能出現的幾種插入情況:
1、初次插入。
2、插入與任何結點不產生地址衝突。
3、產生地址衝突,table陣列下面結點以單鏈表形式存在,插入時直接掛在單鏈表最後。
4、產生地址衝突,key值和之前結點一樣。
5、產生地址衝突,table陣列下面結點以紅黑樹形式存在,插入時需要在樹中查詢合適位置。
下面是對程式碼的分析:
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為空,表明是第一元素插入,則先resize。初次大小預設16。 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 如果要插入結點的位置是空,也就是沒有產生Hash地址衝突,直接放入table。 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { // 如果產生了衝突,那麼有兩種情況。1、key相同。2、key不同。 Node<K,V> e; K k; // 要插入的結點key值和table現有位置上的結點的key值相同,則直接賦值e。p為當前位置上的元素,e為需要被修改的元素。 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) // 如果p是TreeNode的例項,說明p下面掛著紅黑樹,需要在樹中找到一個合適的位置e插入。 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { // p下面的結點數未超過8,則以單向連結串列的形式存在。 if ((e = p.next) == null) { // 逐個往下找到空位置 p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st -1 0 1 2 3 4 5 6 treeifyBin(tab, hash); // TREEIFY_THRESHOLD=8,當容量超過8則轉化成紅黑樹 break; } // 如果與單向連結串列上的某個結點key值相同,則跳出迴圈,此時e是需要修改的結點,p是e的前驅結點。 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; // 如果onlyIfAbsent為True則不改變舊值。如果插入中遇見key一樣的結點,則保留第一次插入的結果。 if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; // 修改次數++ // 如果插入後大小大於threshold,則出發resize if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }