1、ConcurrentHashMap併發容器java
1.ConcurrentHashMapnode
ConcurrentHashMap是HashMap的升級版,HashMap雖然效率高,但它是線程不安全的容器,不能再多線程環境使用,而HashTable雖然是線程安全的,但使用的是效率比較低的synchronized,而且synchronized是悲觀鎖,不論讀寫都是獨佔的。而ConcurrentHashMap的底層是使用CAS來實現併發訪問的,效率較高。算法
ConcurrentHashMap的底層數據結構與HashMap相同,採用數組+鏈表/紅黑樹來實現,以下圖:數組
2.鏈表結點的實現安全
鏈表結點是基本的存儲單元,其實不用想太多,必然和HashMap中的Node相相似。數據結構
//能夠看到Node實現了Map.Entry接口,與HashMap基本同樣 static class Node<K,V> implements Map.Entry<K,V> { final int hash; //hash值,final修飾,表示hash碼不可改變。 final K key; //key值 volatile V val; //value值,volatile保證可見性 volatile Node<K,V> next; //鏈表的下個結點 Node(int hash, K key, V val, Node<K,V> next) { this.hash = hash; this.key = key; this.val = val; this.next = next; } public final K getKey() { return key; } public final V getValue() { return val; } public final int hashCode() { return key.hashCode() ^ val.hashCode(); } public final String toString(){ return key + "=" + val; } public final V setValue(V value) { throw new UnsupportedOperationException(); } //重寫equals方法 public final boolean equals(Object o) { Object k, v, u; Map.Entry<?,?> e; return ((o instanceof Map.Entry) && (k = (e = (Map.Entry<?,?>)o).getKey()) != null && (v = e.getValue()) != null && (k == key || k.equals(key)) && (v == (u = val) || v.equals(u))); } //查找結點,能夠看出ConcurrentHashMap中不容許存放value==null的數據 Node<K,V> find(int h, Object k) { Node<K,V> e = this; if (k != null) { do { K ek; if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; } while ((e = e.next) != null); } return null; } } //底層數組擴容時的過渡鏈表結點類型 static final class ForwardingNode<K,V> extends Node<K,V> { final Node<K,V>[] nextTable; ForwardingNode(Node<K,V>[] tab) { super(MOVED, null, null, null); //標識結點的狀態爲MOVED,即轉移擴容狀態 this.nextTable = tab; } Node<K,V> find(int h, Object k) { // loop to avoid arbitrarily deep recursion on forwarding nodes outer: for (Node<K,V>[] tab = nextTable;;) { Node<K,V> e; int n; if (k == null || tab == null || (n = tab.length) == 0 || (e = tabAt(tab, (n - 1) & h)) == null) return null; for (;;) { int eh; K ek; if ((eh = e.hash) == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; if (eh < 0) { if (e instanceof ForwardingNode) { tab = ((ForwardingNode<K,V>)e).nextTable; continue outer; } else return e.find(h, k); } if ((e = e.next) == null) return null; } } } }
//紅黑樹的結點實現,繼承了Node結點 static final class TreeNode<K,V> extends Node<K,V> { TreeNode<K,V> parent; //父結點的引用 TreeNode<K,V> left; //左孩子 TreeNode<K,V> right; //右孩子 TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; //顏色 TreeNode(int hash, K key, V val, Node<K,V> next, TreeNode<K,V> parent) { super(hash, key, val, next); this.parent = parent; } //查看樹中是否存在hash值爲h,value爲k的結點 Node<K,V> find(int h, Object k) { return findTreeNode(h, k, null); } //查找結點 final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) { //判斷k是否null,不容許放null值 if (k != null) { TreeNode<K,V> p = this; do { int ph, dir; K pk; TreeNode<K,V> q; TreeNode<K,V> pl = p.left, pr = p.right; if ((ph = p.hash) > h) p = pl; else if (ph < h) p = pr; else if ((pk = p.key) == k || (pk != null && k.equals(pk))) return p; else if (pl == null) p = pr; else if (pr == null) p = pl; else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0) p = (dir < 0) ? pl : pr; else if ((q = pr.findTreeNode(h, k, kc)) != null) return q; else p = pl; } while (p != null); } return null; } }
//TreeBin 用做樹的頭結點,只存儲root和first節點,不存儲節點的key、value值 static final class TreeBin<K,V> extends Node<K,V> { TreeNode<K,V> root; //根結點 volatile TreeNode<K,V> first; //樹的鏈式結構 volatile Thread waiter; //等待線程 volatile int lockState; //鎖狀態 // values for lockState static final int WRITER = 1; // 表明擁有寫入鎖 static final int WAITER = 2; // 表明等待獲取寫入鎖 static final int READER = 4; // 用於設置讀取鎖的增長 }
//ConcurrentMap接口源碼 public interface ConcurrentMap<K, V> extends Map<K, V> { //返回指定的key對應的value值,若key不存在則返回默認提供的defaultValue @Override default V getOrDefault(Object key, V defaultValue) { V v; return ((v = get(key)) != null) ? v : defaultValue; } //遍歷Map的方法,至關於for循環或foreach循環,主要是用於lambda表達式 @Override default void forEach(BiConsumer<? super K, ? super V> action) { Objects.requireNonNull(action); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. continue; } action.accept(k, v); } } //若是map中不存在key對應的鍵值對,那麼就新增當前傳入的key和value。 //不然就返回map中key對應的value值 V putIfAbsent(K key, V value); //刪除map中對應的key和value,當且僅當key和value都對應上時才刪除 //若key的對應的值不是value則不刪除 boolean remove(Object key, Object value); //替換key的value值,當key對應的舊值是oldValue時,替換爲newValue //不然不替換 boolean replace(K key, V oldValue, V newValue); //若key存在,則將映射值替換爲給定的value V replace(K key, V value); //將每一個key映射的值替換成給定的調用函數的返回值 @Override default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { Objects.requireNonNull(function); forEach((k,v) -> { //lambda表達式遍歷map while(!replace(k, v, function.apply(k, v))) { // v changed or k is gone if ( (v = get(k)) == null) { // k is no longer in the map. break; } } }); } //若是map中不存在key(或key的映射爲null)且調用key爲參數的函數返回值newValue不爲null //且將key與返回值newValue關聯成功時,返回newValue,不然返回key本來的映射值 @Override default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { Objects.requireNonNull(mappingFunction); V v, newValue; return ((v = get(key)) == null && (newValue = mappingFunction.apply(key)) != null && (v = putIfAbsent(key, newValue)) == null) ? newValue : v; } //調用函數返回的新映射值替換key對應的舊映射值,若果新映射值爲null,則直接將鍵值對從map中刪除 @Override default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue; while((oldValue = get(key)) != null) { V newValue = remappingFunction.apply(key, oldValue); if (newValue != null) { if (replace(key, oldValue, newValue)) return newValue; } else if (remove(key, oldValue)) return null; } return oldValue; } @Override default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue = get(key); //獲取舊映射值 for(;;) { V newValue = remappingFunction.apply(key, oldValue); //計算新映射值 if (newValue == null) { //判斷新映射值是否爲null //新值不存在且map中存在key,直接刪除該鍵值對 if (oldValue != null || containsKey(key)) { //判斷刪除是否成功 if (remove(key, oldValue)) { // removed the old value as expected return null; } //刪除失敗在繼續嘗試 oldValue = get(key); } else { //key本來就不存在,直接返回 return null; } } else { // 新映射值不爲null,且舊映射值存在。則直接替換舊映射值 if (oldValue != null) { // replace if (replace(key, oldValue, newValue)) { // replaced as expected. return newValue; } oldValue = get(key); } else { //舊映射值不存在,即key不存在,則直接向map中新增 if ((oldValue = putIfAbsent(key, newValue)) == null) { return newValue; } } } } } //若是map中存在key且映射爲value,則用調用函數計算出的非空新映射值替換舊映射值 //若新映設置爲null,則將舊鍵值對刪除 //若是map中不存在key或key的舊映射爲null,則直接新增鍵值對(key和value) @Override default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); V oldValue = get(key); for (;;) { if (oldValue != null) { V newValue = remappingFunction.apply(oldValue, value); if (newValue != null) { if (replace(key, oldValue, newValue)) return newValue; } else if (remove(key, oldValue)) { return null; } oldValue = get(key); } else { if ((oldValue = putIfAbsent(key, value)) == null) { return value; } } } } }
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable { //map可達到的最大容量,2的31次方 private static final int MAXIMUM_CAPACITY = 1 << 30; //map初始的默認容量,沒有指定map的容量時的默認值 private static final int DEFAULT_CAPACITY = 16; //數組的最大長度 static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //map的默認併發級別,沒有使用 private static final int DEFAULT_CONCURRENCY_LEVEL = 16; //map的默認負載因子 private static final float LOAD_FACTOR = 0.75f; //鏈表轉換成紅黑樹的臨界鏈表長度,即鏈表長度大於等於8時,鏈表將轉成紅黑樹 static final int TREEIFY_THRESHOLD = 8; //紅黑樹轉成鏈表的臨界結點數,即紅黑樹的結點個數小於等於6時,由紅黑樹退化成鏈表 static final int UNTREEIFY_THRESHOLD = 6; //容許鏈表樹化的最小容量值,即map的容量要大於64,才能將鏈表樹化 static final int MIN_TREEIFY_CAPACITY = 64; //擴容線程所負責的區間大小最低爲16,避免發生大量的內存衝突 private static final int MIN_TRANSFER_STRIDE = 16; //用於生成當前數組對應的基數戳 private static int RESIZE_STAMP_BITS = 16; //表示最多能有多少個線程可以幫助進行擴容,由於sizeCtl只有低16位用於標識,因此最多隻有2^16-1個線程幫助擴容 private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1; //將基數戳左移的位數,保證左移後的基數戳爲負值,而後再加上n+1,表示n個線程正在擴容 private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS; static final int MOVED = -1; // 表示正在轉移,是一個forwardNode節點 static final int TREEBIN = -2; // 表示已經轉換成樹,是一個TreeBin節點 static final int RESERVED = -3; // hash for transient reservations static final int HASH_BITS = 0x7fffffff; // 用於生成hash值 //計算機的核心數 static final int NCPU = Runtime.getRuntime().availableProcessors(); //底層的table結點數組,存儲鍵值對 transient volatile Node<K,V>[] table; //只有當數組處於擴容過程時,nextTable纔不爲null;不然其餘時刻,nextTable爲null; //nextTable主要用於擴容過程當中指向擴容後的新數組 private transient volatile Node<K,V>[] nextTable; /** * 未初始化: * sizeCtl=0:表示沒有指定初始容量。 * sizeCtl>0:表示初始容量。 * 初始化中: * sizeCtl=-1,標記做用,告知其餘線程,正在初始化 * 正常狀態: * sizeCtl=0.75n ,擴容閾值 * 擴容中: * sizeCtl < 0 : 表示有其餘線程正在執行擴容 * sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2 :表示此時只有一個線程在執行擴容 */ private transient volatile int sizeCtl; //用於擴容過程當中,指示原數組下一個分割區間的上界位置,從後往前指示 private transient volatile int transferIndex; //key的視圖,map中全部key的集合 private transient KeySetView<K,V> keySet; //value的視圖 private transient ValuesView<K,V> values; //Entry視圖 private transient EntrySetView<K,V> entrySet; //空構造,啥也沒幹 public ConcurrentHashMap() { } //帶指定容量的構造方法,雖然指定了容量,但map的容量的肯定並非指定的容量 //而是最接近該容量的2的冪次方數 public ConcurrentHashMap(int initialCapacity) { if (initialCapacity < 0) //判斷容量是否合法 throw new IllegalArgumentException(); int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); this.sizeCtl = cap; } //擴容策略,容量的肯定方法 private static final int tableSizeFor(int c) { int n = c - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } //帶有map集合的構造方法 public ConcurrentHashMap(Map<? extends K, ? extends V> m) { this.sizeCtl = DEFAULT_CAPACITY; putAll(m); } //帶有負載因子及初始化容量的構造方法 public ConcurrentHashMap(int initialCapacity, float loadFactor) { this(initialCapacity, loadFactor, 1); } //帶有負載因子及初始化容量、併發等級的構造方法 public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException(); if (initialCapacity < concurrencyLevel) // 最低的容量不能小於併發級別 initialCapacity = concurrencyLevel; // as estimated threads long size = (long)(1.0 + (long)initialCapacity / loadFactor); int cap = (size >= (long)MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)size); this.sizeCtl = cap; } //不安全的屬性變量,知道就行 private static final sun.misc.Unsafe U; //不安全的底層操做類 private static final long SIZECTL; //變量sizeCtl的內存地址偏移量 private static final long TRANSFERINDEX; //變量transferIndex的內存地址偏移量 private static final long BASECOUNT; //變量baseCount的內存地址偏移量 private static final long CELLSBUSY; //變量cellsBusy的內存地址偏移量 private static final long CELLVALUE; //變量value的內存地址偏移量 private static final long ABASE; //變量ak的內存地址偏移量 private static final int ASHIFT; static { try { U = sun.misc.Unsafe.getUnsafe(); Class<?> k = ConcurrentHashMap.class; SIZECTL = U.objectFieldOffset (k.getDeclaredField("sizeCtl")); TRANSFERINDEX = U.objectFieldOffset (k.getDeclaredField("transferIndex")); BASECOUNT = U.objectFieldOffset (k.getDeclaredField("baseCount")); CELLSBUSY = U.objectFieldOffset (k.getDeclaredField("cellsBusy")); Class<?> ck = CounterCell.class; CELLVALUE = U.objectFieldOffset (ck.getDeclaredField("value")); Class<?> ak = Node[].class; ABASE = U.arrayBaseOffset(ak); int scale = U.arrayIndexScale(ak); if ((scale & (scale - 1)) != 0) throw new Error("data type scale not a power of two"); ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); } catch (Exception e) { throw new Error(e); } } }
public V put(K key, V value) { return putVal(key, value, false); //真正執行put的方法 } //若key不存在map中,則直接將key與value新增到map中,若key已存在,則根據onlyIfAbsent //決定是否覆蓋,false爲覆蓋,true不覆蓋 final V putVal(K key, V value, boolean onlyIfAbsent) { //判斷key或value是否爲null,從這能夠看出,ConcurrentHashMap的鍵與值均不能爲null if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); //計算key對應的hash值 int binCount = 0; //用來計算在這個鏈表或紅黑樹總共有多少個元素,用來控制擴容或者轉移爲樹 for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; //判斷table是否初始化過,若未初始化,則進行初始化 if (tab == null || (n = tab.length) == 0) tab = initTable(); //初始哈數組的方法 //判斷hash值在table中對應的索引位置是否已經有數據 //若沒有數據,則直接公國CAS的方式更新數據(即新建一個結點放到數組table中) else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) //向數組中新增結點 break; // 新增完畢,退出循環 } //判斷數組table對應索引的結點的狀態(即鏈表的表頭或紅黑樹的根結點的hash值) //若爲MOVED表示數組正在擴容的複製階段,那麼當前線程也前去幫忙複製 else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); //幫助轉移複製數據 else { V oldVal = null; //對錶頭或樹根結點對象加鎖,即同時僅有一個線程能對該鏈表或紅黑樹進行新增操做 //從這裏能夠看出ConcurrentHashMap的併發安全是基於對鏈表或黑紅樹的獨佔操做實現的 synchronized (f) { //判斷數組中的索引是否發生改變 //有可能結點的類型發生改變(鏈表轉成紅黑樹) if (tabAt(tab, i) == f) { if (fh >= 0) { binCount = 1; //遍歷鏈表 for (Node<K,V> e = f;; ++binCount) { K ek; //判斷要存放的key在鏈表中是否已經存在 //如果存在則根據onlyIfAbsent決定是否覆蓋 if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; //如果key在鏈表中以前不存在,那麼新建一個key和value構成的結點,向鏈表末尾新增 if ((e = e.next) == null) { pred.next = new Node<K,V>(hash, key, value, null); break; } } } //判斷結點是不是TreeBin類型,如果則代表鏈表已經樹化了 //應該使用紅黑樹中的方法進行替換或新增 else if (f instanceof TreeBin) { Node<K,V> p; binCount = 2; //putTreeVal是紅黑樹中的新增或替換方法, //若返回不爲null,則表示key在樹中已存在,返回對應的結點 //若返回是null,代表是新增結點 if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } } } //判斷鏈表是否須要樹化成紅黑樹 if (binCount != 0) { if (binCount >= TREEIFY_THRESHOLD) //判斷鏈表的長度是否超過8 treeifyBin(tab, i); if (oldVal != null) //如果替換結點的value,則返回舊值 return oldVal; break; } } } addCount(1L, binCount); //到這必然新增一個node,該修改size的值了 return null; } //hash值的計算方法,即散列算法 //h縮小2的16次方後與自身向異或,在與上HASH_BITS(0x7fffffff) static final int spread(int h) { return (h ^ (h >>> 16)) & HASH_BITS; } //初始化table數組 private final Node<K,V>[] initTable() { Node<K,V>[] tab; int sc; //當table爲空時,嘗試對table進行初始化 while ((tab = table) == null || tab.length == 0) { //判斷是否有其餘線程正在對table數組進行初始化工做 //當sizeCtl==-1時,說明已經有線程正在對table進行初始化,當前線程應該暫停讓出cpu資源 if ((sc = sizeCtl) < 0) Thread.yield(); //CAS方式嘗試更新sizeCtl的值,將sizeCtl的值由sc更新成-1,-1表示table表要進行初始化了 else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { try { //再次確認table爲初始化過 if ((tab = table) == null || tab.length == 0) { //初始化指定的容量,若未指定初始容量的大小則使用DEFAULT_CAPACITY(16) int n = (sc > 0) ? sc : DEFAULT_CAPACITY; @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = tab = nt; sc = n - (n >>> 2); //令sizeCtl的值爲0.75倍的數組大小 } } finally { sizeCtl = sc; } break; } } return tab; } //安全的獲取數組tab中索引爲i的位置的數據 static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) { return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); } //輔助table數組的擴容轉移工做 final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) { Node<K,V>[] nextTab; int sc; //判斷數組table是否爲null,且結點f是不是ForwardingNode類型,且nextTab是否爲null //即判斷map是否是處在擴容狀態,如果進入幫助擴容 if (tab != null && (f instanceof ForwardingNode) && (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) { int rs = resizeStamp(tab.length); while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) { //判斷是否還有未轉移的桶區間,若沒有,則退出 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || transferIndex <= 0) break; //若還有,則嘗試參與擴容的線程數+1;成功就進行轉移, if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) { transfer(tab, nextTab); break; } } return nextTab; } return table; } //鏈表是否須要樹化 private final void treeifyBin(Node<K,V>[] tab, int index) { Node<K,V> b; int n, sc; if (tab != null) { //判斷數組table的大小是否知足樹化的最小容量要求 //若不知足,但鏈表長度又超過8,則進行數組擴容,擴大一倍 if ((n = tab.length) < MIN_TREEIFY_CAPACITY) tryPresize(n << 1); //擴容 else if ((b = tabAt(tab, index)) != null && b.hash >= 0) { //鎖結點將鏈表樹化。其實就是新建一棵紅黑樹。 synchronized (b) { if (tabAt(tab, index) == b) { TreeNode<K,V> hd = null, tl = null; for (Node<K,V> e = b; e != null; e = e.next) { TreeNode<K,V> p = new TreeNode<K,V>(e.hash, e.key, e.val, null, null); if ((p.prev = tl) == null) hd = p; else tl.next = p; tl = p; } setTabAt(tab, index, new TreeBin<K,V>(hd)); //更新索引位置的結點爲樹的根結點 } } } } } //數組table擴容 private final void tryPresize(int size) { //判斷要擴大的容量是否越界,且是不是2的次方 int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1); int sc; while ((sc = sizeCtl) >= 0) { Node<K,V>[] tab = table; int n; //判斷數組是否初始化過,若沒有初始化過,則初始化數組table //這裏和initTable方法是同樣的 if (tab == null || (n = tab.length) == 0) { n = (sc > c) ? sc : c; if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { try { if (table == tab) { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = nt; sc = n - (n >>> 2); } } finally { sizeCtl = sc; } } } //判斷是否須要擴容,c小於等於sc則表示無需擴容,沒達到擴容的臨界值,n大於MAXIMUM_CAPACITY表示不能再擴大了 else if (c <= sc || n >= MAXIMUM_CAPACITY) break; else if (tab == table) { int rs = resizeStamp(n); //不知道用來幹嗎,艹 //判斷是否在擴容或初始化,擴容或初始化是sc小於0 //如果初始化(sc==-1) if (sc < 0) { Node<K,V>[] nt; /** * 1 (sc >>> RESIZE_STAMP_SHIFT) != rs :擴容線程數 > MAX_RESIZERS-1 * 2 sc == rs + 1 和 sc == rs + MAX_RESIZERS :看不懂.. * 3 (nt = nextTable) == null :表示nextTable正在初始化 * 4 transferIndex <= 0 :表示全部hash桶均分配出去 */ //若是不須要幫其擴容,直接返回 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; //transfer的線程數+1,該線程將幫忙轉移 //在transfer的時候,sc表示在transfer工做的線程數 //第一個執行擴容操做的線程,將sizeCtl設置爲:(resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2) if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); } //沒有在初始化或擴容,則開始擴容 else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } } } //這個方法計算n的二進制最高位前有幾個零,而後和2的15次方按位或,得出個數,不知道有什麼用 static final int resizeStamp(int n) { return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1)); } /** * 數組擴容的核心方法 * 把數組中的節點複製到新的數組的相同位置,或者移動到擴張部分的相同位置 * 在這裏首先會計算一個步長,表示一個線程處理的數組長度,用來控制對CPU的使用, * 每一個CPU最少處理16個長度的數組元素,也就是說,若是一個數組的長度只有16,那只有一個線程會對其進行擴容的複製移動操做 * 擴容的時候會一直遍歷,直到複製完全部節點,每處理一個節點的時候會在鏈表的頭部設置一個fwd節點,這樣其餘線程就會跳過他, * 複製後在新數組中的鏈表不是絕對的反序的 * 簡要過程 * 一、獲取遍歷table的步長 * 二、最早進行擴容的線程,會初始化nextTable(正常狀況下,nextTable 是table的兩倍大小) * 三、計算table某個位置索引 i,該位置上的數據將會被轉移到nextTable 中。 * 四、若是索引i 所對應的table的位置上沒有存放數據,則放有ForwardingNode 數據,代表該table 正在進行擴容處理。 * (若是有添加數據的線程添加數據到該位置上,將會發現table的狀態) * 五、將索引i 位置上的數據進行轉移,數據分紅兩部分,一部分就是數據 在nextTable 中索引沒有變(仍然是i), * 另外一部分則是其索引變成i+n的,將這兩部分分別添加到nextTable 中。 */ private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) { int n = tab.length, stride; //判斷並設置每一個cpu核心(即每一個線程)須要轉移的結點數量,最少爲16個 if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE; // subdivide range //nextTab爲null,初始化一個table數組2倍長度的數組 if (nextTab == null) { // initiating try { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1]; nextTab = nt; } catch (Throwable ex) { // try to cope with OOME sizeCtl = Integer.MAX_VALUE; return; } nextTable = nextTab; transferIndex = n; //transferIndex爲原數組的終點,轉移時,從後往前轉移,控制原數組的轉移 } int nextn = nextTab.length; //新數組長度 //新建ForwardingNode結點,用來控制併發的,當一個節點爲空或已經被轉移以後,就設置爲ForwardingNode節點 //是個空的標誌結點 ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab); //advance 指的是作完了一個位置的遷移工做,能夠準備作下一個位置的了 //表示是否繼續向前查找的標誌位 boolean advance = true; boolean finishing = false; //轉移是否完成的標誌位,完成前從新掃面數組,看看有沒有遺留的 //計算和控制對table 表中位置i 的數據進行轉移 //bound爲數組區間下限值,i爲當前轉移數組的位置,--i處理轉移下一個節點位置,從後往前處理 //1 逆序遷移已經獲取到的hash桶集合,若是遷移完畢,則更新transferIndex,獲取下一批待遷移的hash桶 //2 若是transferIndex=0,表示因此hash桶均被分配,將i置爲-1,準備退出transfer方法 for (int i = 0, bound = 0;;) { Node<K,V> f; int fh; while (advance) { int nextIndex, nextBound; //判斷當前這一段桶區間stride是否轉移完成 //i小於bound,就表示當前任務完成了 //finishing==true則表示沒有轉移任務結束了 if (--i >= bound || finishing) advance = false; //判斷是否還有 轉移任務可領取 //transferIndex==0表示,原數組中全部的桶區間都已經有線程在進行轉移了 else if ((nextIndex = transferIndex) <= 0) { i = -1; advance = false; } //CAS操做修改transferIndex值,表明下一個線程要轉移原數組的結點的起始索引位置 else if (U.compareAndSwapInt (this, TRANSFERINDEX, nextIndex, nextBound = (nextIndex > stride ? nextIndex - stride : 0))) { bound = nextBound; i = nextIndex - 1; advance = false; } } //判斷i是否小於0,便是不是全部數據都轉移完了 //且i是否越界,i是不該該大於原數組長度的,也不應大於新數組長度 if (i < 0 || i >= n || i + n >= nextn) { int sc; //判斷轉移操做是否完成 //完成的話就將新數組賦給table,sizeCtl變爲0.75倍的新數組長度 //nextTable則賦null,擴容完成 if (finishing) { nextTable = null; table = nextTab; sizeCtl = (n << 1) - (n >>> 1); return; } //轉移工做沒有結束,嘗試更新sizeCtl的值,更新成功,表示當前轉移該桶區間數據的任務完成了 /** * 最早的線程,執行transfer方法時,會設置 * sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2) (爲何是這個數?) * 後面輔助擴容的線程,執行transfer方法以前,會設置 sizeCtl = sizeCtl+1 * 每一個線程轉移完桶區間的數據後退出以前,會設置 sizeCtl = sizeCtl-1 * 那麼最後一個線程退出時:必然有sc == (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2), * 即 (sc - 2) == resizeStamp(n) << RESIZE_STAMP_SHIFT */ if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) { //判斷是否到最後一個線程 if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT) return; finishing = advance = true; i = n; //最後退出的線程要從新check下是否所有轉移完畢 } } //判斷原數組索引i處是否爲null,爲null就放個ForwardingNode結點,表示數組正在擴容轉移 else if ((f = tabAt(tab, i)) == null) advance = casTabAt(tab, i, null, fwd); //判斷索引i處的結點是不是ForwardingNode結點,如果,表示該結點已經轉移了 else if ((fh = f.hash) == MOVED) advance = true; // already processed else { //對數組索引i的結點進行加鎖,該結點進行數據轉移,不要打擾 synchronized (f) { //再次判斷頭結點有沒有被修改過,被修改過就循環從新來 if (tabAt(tab, i) == f) { //新建兩個鏈表。用於存放原鏈表轉移到新數組是分開的結點 //ln存放索引不變的結點,hn存放索引變爲i+n的結點 Node<K,V> ln, hn; //判斷頭結點時鏈表仍是紅黑樹 /** * fh大於等於0表示當前索引下是鏈表結構,那麼鏈表中的結點轉移到新數組中 * 只有可能存放在與元素組相同的索引i處,或是i+n(n爲原數組長度)處,這是由於 * 數組的擴容時原來的2倍,且數組的長度必須是2的冪次方,這就使得同一索引的結點在新數組 * 中的索引只有上述兩種可能的去處(這裏與hashmap是相同的) */ if (fh >= 0) { //計算hash值與數組長度的按位結果 /** * 咱們知道hash&(n-1)是結點在數組中的索引位置 * 而hash&n,則是判斷結點是在新數組中位置是否變化的一個依據 * 例子:e.hash=6,二進制爲0000 0110。oldCap假設爲16,二進制位0001 0000。newCap爲32,二進制位0010 0000 * (e.hash & oldCap)=0000 0110 & 0001 0000=0000 0000 ; * (e.hash & oldCap-1)=0000 0110 & 0000 1111=0000 0110 ; * (e.hash & newCap)=0000 0110 & 0010 0000=0000 0000 * (e.hash & newCap-1)=0000 0110 & 0001 1111=0000 0110 ; * 例子:e.hash=19,二進制爲0001 0011。oldCap假設爲16,二進制位0001 0000。newCap爲32,二進制位0010 0000 * (e.hash & oldCap)=0001 0011 & 0001 0000=0001 0000 ; * (e.hash & oldCap-1)=0001 0011 & 0000 1111=0000 0011 ; * (e.hash & newCap)=0001 0011 & 0010 0000=0000 0000 * (e.hash & newCap-1)=0001 0011 & 0001 1111=0001 0011 ; * 有上面就能夠看出當(e.hash & oldCap) == 0時,(e.hash & oldCap-1)與(e.hash & newCap-1)的值時相同的,即在新數組中索引不變 * 而當(e.hash & oldCap) != 0 時,(e.hash & oldCap-1)與(e.hash & newCap-1)的值不相同的,即在新數組的索引變了 * 而且hash& n的結果只有0或n兩種,能夠藉此判斷是存放在hn中仍是ln中 int runBit = fh & n; Node<K,V> lastRun = f; //最後一次發生hash值改變的結點 //遍歷鏈表查找結點的hash & n值最後一次有變化的結點 for (Node<K,V> p = f.next; p != null; p = p.next) { int b = p.hash & n; if (b != runBit) { runBit = b; lastRun = p; } } //lastRun用做哪一個鏈表的起點,runBit爲0表示結點在新數組中的索引不變 //runBit不爲0表示結點在新數組中發生改變,變爲i+n if (runBit == 0) { ln = lastRun; hn = null; } else { hn = lastRun; ln = null; } //將結點轉移到新數組中 //遍歷舊鏈表,按個判斷是移動到i+n位置,仍是索引不動,分別放入ln或hn鏈表中 //這裏hn或ln哪一個鏈表爲null,就會出現結點順序反轉,假設ln爲null,則 //有ln==null,那麼越早加入鏈表就會排在越後面 //而hn==lastRun,則會有部分結點反轉,即原數組中lastRun結點以前的順序會反轉,以後不變 for (Node<K,V> p = f; p != lastRun; p = p.next) { int ph = p.hash; K pk = p.key; V pv = p.val; if ((ph & n) == 0) ln = new Node<K,V>(ph, pk, pv, ln); else hn = new Node<K,V>(ph, pk, pv, hn); } //將兩個新的鏈表更新到新數組中 setTabAt(nextTab, i, ln); setTabAt(nextTab, i + n, hn); setTabAt(tab, i, fwd); //將原數組的索引位置結點用fwd替換 advance = true; } //這裏就是紅黑樹的轉移了 else if (f instanceof TreeBin) { TreeBin<K,V> t = (TreeBin<K,V>)f; TreeNode<K,V> lo = null, loTail = null; TreeNode<K,V> hi = null, hiTail = null; int lc = 0, hc = 0; for (Node<K,V> e = t.first; e != null; e = e.next) { int h = e.hash; TreeNode<K,V> p = new TreeNode<K,V> (h, e.key, e.val, null, null); if ((h & n) == 0) { if ((p.prev = loTail) == null) lo = p; else loTail.next = p; loTail = p; ++lc; } else { if ((p.prev = hiTail) == null) hi = p; else hiTail.next = p; hiTail = p; ++hc; } } //判斷兩個樹結點是否是小於等於6,要不要退化成鏈表 ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : (hc != 0) ? new TreeBin<K,V>(lo) : t; hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : (lc != 0) ? new TreeBin<K,V>(hi) : t; setTabAt(nextTab, i, ln); setTabAt(nextTab, i + n, hn); setTabAt(tab, i, fwd); advance = true; } } } } } }
//標識當前cell數組是否在初始化或擴容中的CAS標誌位 private transient volatile int cellsBusy; //counterCells數組,總數值的分值分別存在每一個cell中 private transient volatile CounterCell[] counterCells; //沒有競爭的時候使用,或者在初始化的時候做爲一個反饋 //總和值的得到方式爲 base + 每一個cell中分值之和在併發度較低的場景下,全部值都直接累加在base中 private transient volatile long baseCount; //CounterCell的定義 @sun.misc.Contended static final class CounterCell { volatile long value; CounterCell(long x) { value = x; } } //計算總和,每一個CounterCell中的值相加 final long sumCount() { CounterCell[] as = counterCells; CounterCell a; long sum = baseCount; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
private final void addCount(long x, int check) { CounterCell[] as; long b, s; //若是counterCell數組爲null,則直接嘗試將增長的鍵值對數更新到baseCount的上 //若counterCell不爲null if ((as = counterCells) != null || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { CounterCell a; long v; int m; // 是否衝突標誌,默認未衝突 boolean uncontended = true; // 若是計數數組是空(還沒有出現併發) // 若是隨機取一個數組位置爲空 或者 // 修改這個變量cellvalue失敗(出現併發了) // 執行 fullAddCount 方法。並結束 if (as == null || (m = as.length - 1) < 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) { fullAddCount(x, uncontended); return; } if (check <= 1) return; //計算總和 s = sumCount(); } //判斷是否須要擴容,從putVal方法進來(check一定大於等於0)一定要檢查是否要擴容 //這裏與上面分析的擴容過程相似,不在多說 if (check >= 0) { Node<K,V>[] tab, nt; int n, sc; while (s >= (long)(sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) { int rs = resizeStamp(n); if (sc < 0) { if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); s = sumCount(); } } } /** * 一、初始化probe,該值散列後用於獲取counterCells數組的索引值(線程相關) * 二、若是counterCells數組不爲空,嘗試佔據counterCells 數組,建立CounterCell存放到counterCells中的某個位置上,退出循環 * 三、若是counterCells 中該位置上存在數據,若是有衝突,則從新循環,不然嘗試累加數據到cell 中,成功則退出循環。 * 四、若是counterCells 數組擴容,或者不須要擴容,則從新循環 * 五、對counterCells 進行擴容操做(容量增長1倍),而後從新循環 * 六、若是counterCells未初始化,則嘗試進行初始化 * 七、若是初始化失敗,則有其它線程再初始化,更新baseCount,成功就退出,不然從新循環。 */ private final void fullAddCount(long x, boolean wasUncontended) { int h; // 獲取當前線程的probe值 // 若是爲0,則初始化當前線程probe值 if ((h = ThreadLocalRandom.getProbe()) == 0) { ThreadLocalRandom.localInit(); // 生成一個線程全部的隨機值 h = ThreadLocalRandom.getProbe(); //返回生成的probe值,用於選擇counterCells數組的下標 wasUncontended = true; //從新生成了probe,未衝突標誌位設置爲true } boolean collide = false; // 標識是不是最後一個槽位 for (;;) { CounterCell[] as; CounterCell a; int n; long v; //判斷counterCells數組是否初始化過,若未初始化則先初始化 //cells數組不爲空,即表示初始化已經成功 if ((as = counterCells) != null && (n = as.length) > 0) { //判斷當前線程對應的索引是否有數據 //沒有數據則新建一個CounterCell存放x,並更新counterCells數組 if ((a = as[(n - 1) & h]) == null) { //判斷counterCells數組在什麼狀態下 //爲0表示不在擴容或初始化狀態 if (cellsBusy == 0) { // Try to attach new Cell //新建當前線程對應的CounterCell對象 CounterCell r = new CounterCell(x); // Optimistic create // CAS設置cellsBusy,防止其它線程來破壞數據結構 if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { boolean created = false; //標誌位,操做是否成功 try { // Recheck under lock CounterCell[] rs; int m, j; //更新counterCells數組中的數據 if ((rs = counterCells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { rs[j] = r; created = true; } } finally { cellsBusy = 0; //恢復標誌位 } //更新成功,則退出死循環 if (created) break; continue; // Slot is now non-empty } } collide = false; } //對應索引中已經有數據了 //而且調用該函數前,cellvalue的CAS操做也已經失敗(已經發生競爭) else if (!wasUncontended) // CAS already known to fail wasUncontended = true; // Continue after rehash //執行value的累加 else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x)) break; //成功更新就直接退出 //若是數組比較大了,則不須要擴容,繼續重試,或者已經擴容了,重試 else if (counterCells != as || n >= NCPU) collide = false; // At max size or stale else if (!collide) collide = true; //對counterCells數組進行擴容,如有其餘線程在擴容則從新循環 else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { try { if (counterCells == as) {// Expand table unless stale CounterCell[] rs = new CounterCell[n << 1]; //擴大1倍 for (int i = 0; i < n; ++i) //直接轉移數據 rs[i] = as[i]; counterCells = rs; } } finally { cellsBusy = 0; } collide = false; continue; // Retry with expanded table } h = ThreadLocalRandom.advanceProbe(h); //從新生成一個probe值 } //進行初始化 //線程要初始化counterCells數組的條件是:cellsBusy標誌位要爲0(說明不在初始化或擴容) //counterCells == as這個判斷不知道有什麼意義? //當前線程更新cellsBusy狀態爲初始化狀態,那麼其餘線程就不能再進行初始化了 else if (cellsBusy == 0 && counterCells == as && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { boolean init = false; //標誌初始化是否成功 try { // Initialize table if (counterCells == as) { CounterCell[] rs = new CounterCell[2]; //counterCells數組默認容量爲2 rs[h & 1] = new CounterCell(x); //h&1是計算線程的probe值散列後的存放索引 counterCells = rs; init = true; //初始化成功 } } finally { cellsBusy = 0; //恢復 } if (init) //判斷初始化是否成功,成功就退出 break; } //有其它線程佔據counterCells數組,直接累加在base變量中 else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x)) break; // Fall back on using base } }
//get方法的實現 public V get(Object key) { Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek; int h = spread(key.hashCode()); //計算key對應的hash值 //判斷table數組是否爲空,若不爲空,h對應的索引是否有結點 //table不爲空或索引位置沒有結點說明key不存在,直接返回null if ((tab = table) != null && (n = tab.length) > 0 && (e = tabAt(tab, (n - 1) & h)) != null) { //判斷該索引位置的結點的hash值是否與h相同(有可能頭結點的hash值小於0,表示此時正在擴容) if ((eh = e.hash) == h) { //判斷要查找的key是否是頭結點,如果頭結點俺麼直接返回頭結點的value值 if ((ek = e.key) == key || (ek != null && key.equals(ek))) return e.val; } //eh小於0,該結點是ForwardingNode結點或TreeBin結點,調用find方法 //ForwardingNode的find方法比較簡單就是遍歷新數組來查找對應結點是否存在 //TreeBin的find方法則要複雜 else if (eh < 0) return (p = e.find(h, key)) != null ? p.val : null; //遍歷鏈表來查找key對應的value while ((e = e.next) != null) { if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) return e.val; } } return null; } //ForwardingNode中的find方法,遍歷新數組對應的索引查找key Node<K,V> find(int h, Object k) { // loop to avoid arbitrarily deep recursion on forwarding nodes outer: for (Node<K,V>[] tab = nextTable;;) { Node<K,V> e; int n; if (k == null || tab == null || (n = tab.length) == 0 || (e = tabAt(tab, (n - 1) & h)) == null) return null; for (;;) { int eh; K ek; if ((eh = e.hash) == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; if (eh < 0) { if (e instanceof ForwardingNode) { tab = ((ForwardingNode<K,V>)e).nextTable; continue outer; } else return e.find(h, k); } if ((e = e.next) == null) return null; } } } //TreeBin中的find方法 final Node<K,V> find(int h, Object k) { if (k != null) { // for (Node<K,V> e = first; e != null; ) { int s; K ek; //判斷TreeBin的狀態,如果等待或寫狀態,則以鏈表的形式進行遍歷查找 //((s = lockState) & (WAITER|WRITER)) == 0表示TreeBin如今處於讀狀態 if (((s = lockState) & (WAITER|WRITER)) != 0) { if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; e = e.next; } //嘗試更新讀狀態,即獲取讀鎖,成功的話,就以紅黑樹的形式進行遍歷查找 else if (U.compareAndSwapInt(this, LOCKSTATE, s, s + READER)) { TreeNode<K,V> r, p; try { p = ((r = root) == null ? null : r.findTreeNode(h, k, null)); } finally { Thread w; if (U.getAndAddInt(this, LOCKSTATE, -READER) == (READER|WAITER) && (w = waiter) != null) LockSupport.unpark(w); } return p; } } } return null; }
public V remove(Object key) { return replaceNode(key, null, null); } final V replaceNode(Object key, V value, Object cv) { int hash = spread(key.hashCode()); //計算hash值 for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; //判斷table數組是否爲空,或hash值對應索引上是否有結點 //table爲空或索引上沒有結點,代表key不存在,直接返回 if (tab == null || (n = tab.length) == 0 || (f = tabAt(tab, i = (n - 1) & hash)) == null) break; else if ((fh = f.hash) == MOVED) //若是當前table數組在擴容,當前線程先去輔助擴容,擴容完在刪除結點 tab = helpTransfer(tab, f); else { V oldVal = null; boolean validated = false; synchronized (f) { //加鎖,保證對應的鏈表或紅黑樹的併發操做 if (tabAt(tab, i) == f) { //再次判斷頭結點是否被改變 //判斷是鏈表仍是紅黑樹結構 //fh大於0代表是鏈表 if (fh >= 0) { validated = true; //遍歷鏈表 for (Node<K,V> e = f, pred = null;;) { K ek; //判斷遍歷到的當前結點是否是要刪除的結點 //是要刪除的結點就進行刪除,並將刪除結點的value返回 if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { V ev = e.val; if (cv == null || cv == ev || (ev != null && cv.equals(ev))) { oldVal = ev; if (value != null) e.val = value; else if (pred != null) pred.next = e.next; else setTabAt(tab, i, e.next); } break; } pred = e; if ((e = e.next) == null) //判斷下個結點 break; //鏈表到頭了,說明不存在喲啊刪除的結點,直接退出循環 } } //底層是紅黑樹結構,執行紅黑樹中刪除節點的額方法,這裏不作深刻,有興趣能夠看源碼具體實現 else if (f instanceof TreeBin) { validated = true; TreeBin<K,V> t = (TreeBin<K,V>)f; TreeNode<K,V> r, p; //先查找出key對應的結點是否存在,不存在直接結束 //存在的話,獲取到value再刪除結點 if ((r = t.root) != null && (p = r.findTreeNode(hash, key, null)) != null) { V pv = p.val; if (cv == null || cv == pv || (pv != null && cv.equals(pv))) { oldVal = pv; if (value != null) p.val = value; else if (t.removeTreeNode(p)) setTabAt(tab, i, untreeify(t.first)); } } } } } //判斷要刪除的key是否存在 //存在就返回對應的value,不存在就返回null if (validated) { if (oldVal != null) { if (value == null) addCount(-1L, -1); return oldVal; } break; } } } return null; }