/** * 經常使用方法put */ public V put(K key, V value) { // 獲取key的hash並命令putval方法替換現有值與非建立模式 return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { // tab - 當前hash桶的引用 // p - key所表明的節點(此節點不必定是目標節點,而僅僅是hash與桶長度的計算值相同而已)(它不爲空時多是鏈表或紅黑樹) // n - 當前桶的容量 // i - key在桶中的下標(同p,不表明目標節點) Node<K,V>[] tab; Node<K,V> p; int n, i; // 初始化局部變量tab並判斷是否爲空,初始化局部變量n並判斷是否爲0 // PS: 源碼中大量的使用了這種書寫方法,不知道放在某寫大廠裏會怎麼樣(斜眼笑) if ((tab = table) == null || (n = tab.length) == 0) // 當tab爲空或n爲0時,代表hash桶還沒有初始化,調用resize()方法,進行初始化並再次初始化局部變量tab與n n = (tab = resize()).length; // 初始化p與i // 這裏使用了(n - 1) & hash的方式計算key在桶中的下標.這個在後面單獨說明 // 當p是否爲空 if ((p = tab[i = (n - 1) & hash]) == null) // p爲空,調用newNode方法初始化節點並賦值到tab對應下標 tab[i] = newNode(hash, key, value, null); else { // p不爲空,發生碰撞.進行後續處理 // e - 目標節點 // k - 目標節點的key Node<K,V> e; K k; // 判斷key是否相同.(這裏除了比較key之外,還比較了hash) // 注意,這裏同時初始化了局部變量k,可是在第二組條件不知足的狀況下,沒有使用價值,能夠被忽略 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // key相同,將e(目標節點)設置爲p e = p; // 判斷節點是不是紅黑樹 else if (p instanceof TreeNode) // 肯定時,直接委派處理 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 走到這裏,表明當前節點爲普通鏈表,進行遍歷查找 // 變量binCount只做爲是否達到tree化的閾值判斷條件. for (int binCount = 0; ; ++binCount) { // 獲取鏈表的下一個元素,並賦值到e(此時e是一箇中間變量,不肯定是不是目標節點) // 第一次for循環時,p表明hash桶中的節點(同時也是鏈表的頭部節點),以後一直等於p.next if ((e = p.next) == null) { // 鏈表遍歷到末尾 // 向鏈表中追加新的節點 p.next = newNode(hash, key, value, null); // 判斷當前鏈表長度是否達到tree閾值 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st // 調用treeifyBin方法直接處理 treeifyBin(tab, hash); // 中斷循環 // 注意,此時局部變量e=null break; } // 能走到此處,說明鏈表未結束,比較e的k是否相同(hash與==) if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) // key相同 break; // e既不爲null也不是目標節點,賦值到p,住被進行下次循環 p = e; } } // 判斷e是否存在 if (e != null) { // existing mapping for key // e不等於null說明操做爲"替換" // 緩存老值 V oldValue = e.value; // 判斷是否必須替換或老值爲null if (!onlyIfAbsent || oldValue == null) // 必須替換或老值爲空,更新節點e的value e.value = value; // 調用回調 afterNodeAccess(e); // 返回老值 // 注意,這裏直接返回了,而沒有進行modCount更新與下面的後續操做 return oldValue; } } // 除了更新鏈表節點之外,都會走到這裏(putTreeVal的返回值是什麼有待確認) // modCount+1 ++modCount; // size+1(元素數量+1) // 判斷是否超過閾值 if (++size > threshold) // 重置大小 resize(); // 調用後置節點插入回調 afterNodeInsertion(evict); return null; }