【Java X 原始碼剖析】Map的原始碼分析--JDK1.8-仍在更新

TypantK發表於2019-03-13

Map介面下的結構

 

 

目錄

HashMap(雜湊表+連結串列or紅黑樹)

①public V Put(K key, V value)

②resize()

③public V get(Object key)

④public boolean containsKey(Object Key)

⑤public V remove(Object key)

public boolean remove(Object key, Object value)

 

LinkedHashMap(雜湊表+非迴圈雙向連結串列)[1.8不是迴圈]

① public V put(K key, V value)

②public V get(Object key)

③public V remove(Object key)

 

TreeMap(紅黑樹)

①public V put(K key, V value)

② final int compare(Object k1, Object k2)

③private void fixAfterInsertion(Entry x),v>

④public V get(Object key) 

⑤public V remove(Object key)

 

HashTable(陣列+連結串列)[Synchronized實現執行緒安全-鎖住整個table]

①public synchronized V put(K key, V value)

 

ConcurrentMap

ConcurrentHashMap

建構函式

①public V put(K key, V value)

ConcurrentSkipListMap

 



HashMap(雜湊表+連結串列or紅黑樹)

通過Node陣列進行儲存,雜湊衝突就通過連結串列或者紅黑樹進行儲存

Node<K,V>實現了父類介面Map.Entry<K,V>

 

**陣列下標index的求值:

 

①public V Put(K key, V value)

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

static final int hash(Object key) {
        int h;
        //hashCode()是本地方法
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }


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;

        //如果根據hash值取得的結點為null,就新建一個結點p,否則返回結點p(注意p在tab的位置)
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //如果插入的結點hash和key都相同
            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) {
                    //已經遍歷到最後一個結點p了,e為p.next,也就是鏈尾插入e
                    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;
                    }
                    //這時e已經等於p.next了,如果連結串列中有相同的key,就退出
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    //繼續遍歷下一個連結串列結點
                    p = e;
                }
            }
            
            //如果不為null,此時e指向key相同hash相同的結點
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    //覆蓋舊值
                    e.value = value;

                //子類LinkedHashMap中重寫,HashMap中是空函式
                afterNodeAccess(e);

                //返回舊值
                return oldValue;
            }
        }
        //更改操作次數
        ++modCount;
        //大於儲存空間臨界值,就擴容,並將原先陣列元素放到新陣列中
        //因為有連結串列、紅黑樹之類,所以還要調整他們
        if (++size > threshold)
            resize();

        //子類LinkedHashMap中重寫,HashMap中是空函式
        afterNodeInsertion(evict);

        return null;
    }

借鑑一張圖,侵權即刪put操作流程圖

②final void treeifyBin(Node<K,V>[] tab, int hash) 

     //樹化前預備操作
     //插入一個元素之後,判斷hash對應桶結點數>=8就執行此函式進行連結串列樹化
     final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                //此方法返回一個有e資料(hash/key/value)的樹節點p,null傳入的是e.next
                TreeNode<K,V> p = replacementTreeNode(e, null);
                //遍歷的第一層:相當於設定根結點hd
                if (tl == null)
                    hd = p;
                else {
                    //和原連結串列順序一致:雙向連結串列
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            
            //進行樹化
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

    // For treeifyBin
    TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
        return new TreeNode<>(p.hash, p.key, p.value, next);
    }


③final void treeify(Node<K,V>[] tab)

        /**
         * Forms tree of the nodes linked from this node.
         * @return root of tree
         */
        //此時連結串列已經是雙向連結串列了
        final void treeify(Node<K,V>[] tab) {
            TreeNode<K,V> root = null;

            //呼叫此方法時用的是頭結點Node.treeify(),所以this代表需要樹化雙向連結串列頭結點
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
                next = (TreeNode<K,V>)x.next;
                x.left = x.right = null;

                //迴圈第一層:設定頭結點
                if (root == null) {
                    x.parent = null;
                    x.red = false;
                    root = x;
                }
                else {
                    K k = x.key;
                    int h = x.hash;
                    Class<?> kc = null;

                    //p是從根結點用來搜尋x應該插入到哪的指標
                    for (TreeNode<K,V> p = root;;) {
                        int dir, ph;
                        K pk = p.key;
                        
                        //*注意:hash不等同於陣列下標,所以hash是可以不同的,陣列下標是一定相同的
                        //dir == -1 代表往p的右走
                        if ((ph = p.hash) > h)
                            dir = -1;

                        //dir == 1 代表往p的左走
                        else if (ph < h)
                            dir = 1;

                        //走到這裡代表x的hash和p的hash相同,就比較key
                        else if ((kc == null &&
                                    //如果key沒有實現comparable介面或者 x和p的key也是相同的
                                  (kc = comparableClassFor(k)) == null) ||
                                 (dir = compareComparables(kc, k, pk)) == 0)
                            //定義的規則(比較判空+類名+hashCode())進行判斷走左還是走右
                            dir = tieBreakOrder(k, pk);

                        //記錄父節點xp
                        TreeNode<K,V> xp = p;
                        //代表x就插入在xp的下層
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
                            x.parent = xp;
                            if (dir <= 0)
                                xp.left = x;
                            else
                                xp.right = x;
                            //紅黑樹插入平衡(通過左右旋和改變結點顏色保證符合紅黑樹要求)
                            root = balanceInsertion(root, x);
                            break;
                        }
                    }
                }
            }
            moveRootToFront(tab, root);
        }

        static int tieBreakOrder(Object a, Object b) {
            int d;
            if (a == null || b == null ||
                //字典序法比較兩個類名是否一致,如果返回0代表類相同
                (d = a.getClass().getName().
                 compareTo(b.getClass().getName())) == 0)
                //通過hashcode()大小來比較
                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                     -1 : 1);
            return d;
        }

 

④static <K,V> TreeNode<K,V> balanceInsertion

                                                  (TreeNode<K,V> root,  TreeNode<K,V> x)

        //每次插入一個紅黑樹結點就呼叫此方法進行一次調整,使其滿足紅黑樹要求
        //root是紅黑樹根結點,x是新插入的結點。最後返回撥整後紅黑樹根結點
        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {

            //新節點設定成紅色
            x.red = true;

            //xp是x的父節點/xpp是xp的父節點/xppl是xpp左結點/xppr是xpp右結點
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {

                //紅黑樹只有一個結點x,顏色設定成黑色(紅黑樹根節點是黑色)
                if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }

                //只有兩層(xp->x)
                else if (!xp.red || (xpp = xp.parent) == null)
                    return root;

                //如果x父節點是x爺結點的左節點
                if (xp == (xppl = xpp.left)) {
                    if ((xppr = xpp.right) != null && xppr.red) {
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.right) {
                            root = rotateLeft(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else {
                    if (xppl != null && xppl.red) {
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.left) {
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

 

⑤static <K,V> void moveRootToFront

                                 (Node<K,V>[] tab, TreeNode<K,V> root)

        /**
         * Ensures that the given root is the first node of its bin.
         */
        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
            int n;
            if (root != null && tab != null && (n = tab.length) > 0) {
                int index = (n - 1) & root.hash;
                TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
                if (root != first) {
                    Node<K,V> rn;
                    tab[index] = root;
                    TreeNode<K,V> rp = root.prev;
                    if ((rn = root.next) != null)
                        ((TreeNode<K,V>)rn).prev = rp;
                    if (rp != null)
                        rp.next = rn;
                    if (first != null)
                        first.prev = root;
                    root.next = first;
                    root.prev = null;
                }
                assert checkInvariants(root);
            }
        }

 

③final TreeNode<K,V> putTreeVal

                             (HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v) 

        /**
         * Tree version of putVal.
         */
        final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                       int h, K k, V v) {
            Class<?> kc = null;
            boolean searched = false;
            TreeNode<K,V> root = (parent != null) ? root() : this;
            for (TreeNode<K,V> p = root;;) {
                int dir, ph; K pk;
                if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;
                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                    return p;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) {
                    if (!searched) {
                        TreeNode<K,V> q, ch;
                        searched = true;
                        if (((ch = p.left) != null &&
                             (q = ch.find(h, k, kc)) != null) ||
                            ((ch = p.right) != null &&
                             (q = ch.find(h, k, kc)) != null))
                            return q;
                    }
                    dir = tieBreakOrder(k, pk);
                }

                TreeNode<K,V> xp = p;
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    Node<K,V> xpn = xp.next;
                    TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    xp.next = x;
                    x.parent = x.prev = xp;
                    if (xpn != null)
                        ((TreeNode<K,V>)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x));
                    return null;
                }
            }
        }

 

②resize()

//初始化(當前table==null)或者擴容之後元素調整(++size > threshold --> resize())
final Node<K,V>[] resize() {
        //oldTab是舊元素陣列
        Node<K,V>[] oldTab = table;
        
        //舊陣列長度
        int oldCap = (oldTab == null) ? 0 : oldTab.length;

        //舊陣列擴容臨界值
        int oldThr = threshold;
        
        //新陣列長度以及擴容值
        int newCap, newThr = 0;

        //如果舊陣列不為空
        if (oldCap > 0) {
            
            //陣列的長度已經達到最大值,就改變臨界值為Integer.MAX_VALUE,返回舊陣列
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }

            //1.新陣列擴容1倍後小於陣列最大值  2.舊陣列長度大於預設初始值
            //就把新陣列的臨界值也擴大一倍
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold

        }
        
        //如果舊陣列為空,且臨界值大於0,新陣列長度就等於舊陣列的擴容臨界值
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;

        //舊陣列為空,臨界值≤0,新陣列長度和臨界值都使用預設值
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }

        //如果新陣列臨界值還為0,就設定根據loadFactor(負載因子)臨界值(其實好像不可能發生?上面的情況要不就return,要不都有賦值)
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        //更新裝填因子
        threshold = newThr;
            //建表
         @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

        //對新表的初始化
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    //如果當前節點沒有雜湊衝突直接插入就行
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    
                    //雜湊衝突:紅黑樹調整
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    
                    //雜湊衝突:連結串列調整
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        //遍歷當前雜湊衝突連結串列
                        do {
                            next = e.next;
                            
                            //用於計算擴容新增後計算位置bit如果為0,就保持原位置(陣列標識)
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            //如果新增bit後計算位置bit為1,就是原位置+舊陣列長度(陣列標識)
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

**擴容後新結點的位置判斷:if(e.hash & oldCap == 0){}

  • n-1:n是當前tab的長度
  • hash(key1):key1的hash值
  • hash(key2):key2的hash值

可以看出擴容之後(b)只有部分資料(key2)的陣列index會發生改變,key1保持不變

而發生變化的就是hash(key)&oldCap這一位bit,所以只需要判斷這一位就知道位置是否變化

而位置變化的大小就是原位置+oldCap

 

 

③public V get(Object key)

public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        
        //判斷必要欄位是否已經初始化,如果沒有直接返回null
        if ((tab = table) != null && (n = tab.length) > 0 &&
            //注意這裡對first的賦值,插入時也是用長度和hash進行與運算來得出tab位置
            (first = tab[(n - 1) & hash]) != null) {

            //首先都是檢查首結點
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;

            //存在雜湊衝突狀況
            if ((e = first.next) != null) {
                
                //判斷是不是紅黑樹
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);

                //不是紅黑樹就當做連結串列來處理(key和hash相同就返回)
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

 

④public boolean containsKey(Object Key)

public boolean containsKey(Object key) {
        return getNode(hash(key), key) != null;
    }


final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;

        //先判斷是否為空,然後拿到hash相同的頭結點
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            
            //先檢查頭結點是否key也和傳進來的key相同
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            //如果和頭結點不相同,繼續往深查詢
            if ((e = first.next) != null) {
                //紅黑樹查詢
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                //連結串列查詢
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

 

⑤public V remove(Object key)

public boolean remove(Object key, Object value)

public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }

final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
            
        //還是先檢查必要的引數table和table.length,index是所要刪除元素的陣列下標
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; K k; V v;
        
            //還是先檢查頭結點的key和hash是否和要求一致,如果一致就賦值node指向要刪除的結點
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            //如果不一致,可能在頭結點的後面,往深找
            else if ((e = p.next) != null) {
                //如果是紅黑樹結構,就呼叫getTreeNode()來找結點返回給node
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    //如果是連結串列結構的處理,找到結點就賦值給node。迴圈中以及迴圈外p.next=e
                    //如果第一次執行就break了,代表只有一個節點?
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            //如果node不為空,說明找到了要刪除的結點
            //matchValue為是否需要對比value值(remove兩個方法不同值)
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                //紅黑樹結構刪除
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                //如果頭結點就是所要刪除結點
                else if (node == p)
                    tab[index] = node.next;
                //node不是首節點:p是node的父節點,直接改變一下指標即可
                else
                    p.next = node.next;
                //HashMap的操作次數+1
                ++modCount;
                --size;
                //讓子類LinkedHashMap重寫,HashMap中沒有實現
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

 

 

LinkedHashMap(雜湊表+非迴圈雙向連結串列)[1.8不是迴圈]{有序}

Node(Entry):

這裡的prev就是before,next就是after,hnext就是next(原HashMap中解決雜湊衝突的指標)

*圖中省略了Key/Value

 

*head和tail只有一個結點時指向同一個結點

 

*accessOrder為true:連結串列根據訪問順序排序(LRU,最新訪問放後面);為false:連結串列根據插入順序排序

 

① public V put(K key, V value)

LinkedHashMap並沒有重寫put方法,但是重寫了構造結點的幾個方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    ...
    tab[i] = newNode(hash, key, value, null);
    ...
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    ...
    if ((e = p.next) == null) {
      p.next = newNode(hash, key, value, null);
    ...
        afterNodeAccess(e);
    ...
        afterNodeInsertion(evict);
      return null;
}
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

// link at the end of list
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        //集合之前是空的
        if (last == null)
            head = p;
        else {
        //將新節點連結到尾部
            p.before = last;
            last.after = p;
        }
    }
void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

②public V get(Object key)

void afterNodeAccess(Node<K,V> e)

*getNode(int hash,Object key)和HashMap的實現一樣,就不重複了

public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }


 void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        //如果是訪問順序排序,才進行排序;如果最後一個結點已經是e就不用排序了
        if (accessOrder && (last = tail) != e) {
            //用p指向e
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;

            //情況1:p前一個結點b不存在
            if (b == null)
                head = a;
            else
                b.after = a;
            
            if (a != null)
                a.before = b;
            //情況2:p後一個結點a不存在
            else
                last = b;
    
            //last==null代表只有一個結點p
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            
            //新的尾結點變成p
            tail = p;
            ++modCount;
        }
    }

③public V remove(Object key)

void afterNodeRemoval(Node<K,V> e)

*getNode(int hash,Object key)和HashMap的實現一樣,就不重複了

void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        //刪除節點後日常兩種情況:前面結點是否空,後面結點是否空
        if (b == null)
            head = a;
        else
            b.after = a;

        if (a == null)
            tail = b;
        else
            a.before = b;
    }

 

TreeMap(紅黑樹){有序}

可以對新增進來的元素進行排序[二叉搜尋樹]

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
    
    //比較器
    private final Comparator<? super K> comparator;

    //根結點
    private transient Entry<K,V> root;

    /**
     * 結點個數
     */
    private transient int size = 0;

    /**
     * 樹結構被修改次數
     */
    private transient int modCount = 0;

    //紅黑樹節點顏色
    private static final boolean RED   = false;
    private static final boolean BLACK = true;

    static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;
        Entry<K,V> right;
        Entry<K,V> parent;
        boolean color = BLACK;
    }
}

①public V put(K key, V value)

public V put(K key, V value) {
        Entry<K,V> t = root;
        
        //如果根結點為空,就設定插入的元素為根結點root,設定必要引數size/modCount後返回
        if (t == null) {
    
            //....
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        //提取出比較器
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;

        //如果比較器不為空,就採用比較器裡面的排序演算法進行排序
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);

                //排序結果小於0:新增節點的key小於當前根結點,往左子樹找插入位置
                if (cmp < 0)
                    t = t.left;
                 //排序結果大於0:新增節點的key大於當前根結點,往右子樹找插入位置
                else if (cmp > 0)
                    t = t.right;
                //兩個key相同,新值覆蓋舊值,返回舊值
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //如果比較器為空,就採用預設的排序演算法
        else {
            if (key == null)
                throw new NullPointerException();

            //獲得比較器
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            //和上面處理步驟一樣
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //執行到這裡,代表key沒有重複,需要新插入結點,所以指定parent為其父節點
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        //插入完引數設定
        size++;
        modCount++;
        return null;
    }

 

② final int compare(Object k1, Object k2)

/**
     * Compares two keys using the correct comparison method for this TreeMap.
     * 通過正確的比較器來比較兩個key:如果TreeMap比較器為空,就用key類實現的比較器;如果不為空,就直接用來比較
     */
    @SuppressWarnings("unchecked")
    final int compare(Object k1, Object k2) {
        //如果
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
    }

 

③private void fixAfterInsertion(Entry<K,V> x)

/** From CLR */
//CLR:公共語言執行庫和Java虛擬機器一樣也是一個執行時環境,它負責資源管理(記憶體分配和垃圾收集等),並保證應用和底層作業系統之間必要的分離
    private void fixAfterInsertion(Entry<K,V> x) {

        //新增的結點設定成紅色
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

 

④public V get(Object key) 

public V get(Object key) {
        Entry<K,V> p = getEntry(key);
        return (p==null ? null : p.value);
    }

final Entry<K,V> getEntry(Object key) {
        //如果有自定義比較器物件,分離出來執行另一段邏輯相同程式碼(為了效能)
        // Offload comparator-based version for sake of performance
        if (comparator != null)
            return getEntryUsingComparator(key);
        if (key == null)
            throw new NullPointerException();

        //提出當前key的型別實現的比較器k
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        Entry<K,V> p = root;
        
        //深度遍歷
        while (p != null) {
            int cmp = k.compareTo(p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }

/**
     * Version of getEntry using comparator. Split off from getEntry
     * for performance. (This is not worth doing for most methods,
     * that are less dependent on comparator performance, but is
     * worthwhile here.)
     * 為了效能將此方法從getEntry()分離出來,大部分方法不依賴於效能的話就沒什麼意義,反之則有意義
     */
final Entry<K,V> getEntryUsingComparator(Object key) {
        //邏輯也是差不多的,執行此方法代表有自定義的比較器
        @SuppressWarnings("unchecked")
            K k = (K) key;
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            Entry<K,V> p = root;
            while (p != null) {
                int cmp = cpr.compare(k, p.key);
                if (cmp < 0)
                    p = p.left;
                else if (cmp > 0)
                    p = p.right;
                else
                    return p;
            }
        }
        return null;
    }

 

⑤public V remove(Object key)

public V remove(Object key) {
        Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;

        V oldValue = p.value;
        deleteEntry(p);
        return oldValue;
    }

/**
     * Delete node p, and then rebalance the tree.
     */
    private void deleteEntry(Entry<K,V> p) {
        modCount++;
        size--;

        // If strictly internal, copy successor's element to p and then make p
        // point to successor.
        if (p.left != null && p.right != null) {
            Entry<K,V> s = successor(p);
            p.key = s.key;
            p.value = s.value;
            p = s;
        } // p has 2 children

        // Start fixup at replacement node, if it exists.
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);

        if (replacement != null) {
            // Link replacement to parent
            replacement.parent = p.parent;
            if (p.parent == null)
                root = replacement;
            else if (p == p.parent.left)
                p.parent.left  = replacement;
            else
                p.parent.right = replacement;

            // Null out links so they are OK to use by fixAfterDeletion.
            p.left = p.right = p.parent = null;

            // Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement);
        } else if (p.parent == null) { // return if we are the only node.
            root = null;
        } else { //  No children. Use self as phantom replacement and unlink.
            if (p.color == BLACK)
                fixAfterDeletion(p);

            if (p.parent != null) {
                if (p == p.parent.left)
                    p.parent.left = null;
                else if (p == p.parent.right)
                    p.parent.right = null;
                p.parent = null;
            }
        }
    }

 

 

HashTable(陣列+連結串列)[Synchronized實現執行緒安全-鎖住整個table]

key-value都不能為null

Hashtable 中的方法是Synchronize的,而HashMap中的方法在預設情況下是非Synchronize的。在多執行緒併發的環境下,可以直接使用Hashtable

Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但二者都實現了Map介面

基本的引數都是一樣的,引數名從Node改成了Entry

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

    /**
     * The hash table data.
     * 雜湊表資料用一個陣列存起來
     */
    private transient Entry<?,?>[] table;

    /**
     * The total number of entries in the hash table.
     * 資料總數
     */
    private transient int count;

    /**
     * The table is rehashed when its size exceeds this threshold.  (The
     * value of this field is (int)(capacity * loadFactor).)
     * 擴容臨界值
     * @serial
     */
    private int threshold;

    /**
     * The load factor for the hashtable.
     * 擴容因子
     * @serial
     */
    private float loadFactor;

    /**
     * The number of times this Hashtable has been structurally modified
     * Structural modifications are those that change the number of entries in
     * the Hashtable or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the Hashtable fail-fast.  (See ConcurrentModificationException).
     * 操作次數
     */
    private transient int modCount = 0;

    private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;
    }
}

 

①public synchronized V put(K key, V value)

//HashTable不同於HashMap:使用synchronized關鍵字保證執行緒同步
public synchronized V put(K key, V value) {
        
        // Make sure the value is not null
        //HashTable不同於HashMap:不接受value為null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
    
        //返回物件的32位jvm記憶體地址
        //HashTable不同於HashMap:不接受key為null,隱式丟擲空指標異常
        int hash = key.hashCode();

        //HashTable不同於HashMap:HashMap陣列下標計算i = (n - 1) & hash]
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];

        //遍歷連結串列。新值覆蓋舊值,返回舊值
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        //沒有相同index又相同key,呼叫方法addEntry()進行判斷是否擴容然後插入
        addEntry(hash, key, value, index);
        return null;
    }

//執行此函式代表要多一個結點了
private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;

        //判斷是否需要擴容(是否≥臨界值)
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

/**
     * Increases the capacity of and internally reorganizes this
     * hashtable, in order to accommodate and access its entries more
     * efficiently.  This method is called automatically when the
     * number of keys in the hashtable exceeds this hashtable's capacity
     * and load factor.
     * 用於重新hash
     */
    @SuppressWarnings("unchecked")
    protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        //和HashMap不一樣的地方:設定的新容量是*2+1
        int newCapacity = (oldCapacity << 1) + 1;

        //如果擴容之後,已經大於陣列的最大值了(Integer.MAX_VALUE - 8)
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            //如果舊陣列已經擴容到最大就繼續用
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            //還沒到最大新陣列容量就擴容成最大
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        //遍歷舊陣列的每個Entry
        for (int i = oldCapacity ; i-- > 0 ;) {
            //遍歷雜湊衝突中連結串列的每個數
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                //重新計算陣列下標後插入,如果又是同一條連結串列,插入的順序和之前的相反
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

 

②public synchronized V get(Object key)

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key.equals(k))},
     * then this method returns {@code v}; otherwise it returns
     * {@code null}.  (There can be at most one such mapping.)
     *
     * @param key the key whose associated value is to be returned
     * @return the value to which the specified key is mapped, or
     *         {@code null} if this map contains no mapping for the key
     * @throws NullPointerException if the specified key is null
     * @see     #put(Object, Object)
     */
    @SuppressWarnings("unchecked")
    public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();

        //很普通地求出陣列下標
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            
            //很普通地遍歷然後找key和hash都相同的value
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }

        //很普通地找不到返回null
        return null;
    }

 

ConcurrentMap

ConcurrentHashMap

*JDK1.7(獲取的是Segment鎖

多執行緒環境下:並不需要爭奪同一個鎖,HashTable的話效率太低(鎖住整個table)

整個 Hash 表被分成多個段,每個段中會對應一個 Segment 段鎖,段與段之間可以併發訪問,但是多執行緒想要操作同一個段是需要獲取鎖的。所有的 put,get,remove 等方法都是根據鍵的 hash 值對應到相應的段中,然後嘗試獲取鎖進行訪問。

*JDK1.8

 取消了基於 Segment 的分段鎖思想,改用 CAS + synchronized 控制併發操作,在某些方面提升了效能。並且追隨 1.8 版本的 HashMap 底層實現,使用陣列+連結串列或紅黑樹進行資料儲存。

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {

    /**
     * The largest possible table capacity.  This value must be
     * exactly 1<<30 to stay within Java array allocation and indexing
     * bounds for power of two table sizes, and is further required
     * because the top two bits of 32bit hash fields are used for
     * control purposes.
     */
    //整個Node陣列的最大範圍,最高的兩位bits要用於控制
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The default initial table capacity.  Must be a power of 2
     * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
     */
    //預設的Node陣列容量,必須是2次冪
    private static final int DEFAULT_CAPACITY = 16;

    /**
     * The largest possible (non-power of two) array size.
     * Needed by toArray and related methods.
     */
    //陣列的最大容量(不是Node陣列),要留8個位元組控制陣列物件
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * The default concurrency level for this table. Unused but
     * defined for compatibility with previous versions of this class.
     */
    //預設併發級別:此版本已經不用,是為了相容以前版本
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    /**
     * The load factor for this table. Overrides of this value in
     * constructors affect only the initial table capacity.  The
     * actual floating point value isn't normally used -- it is
     * simpler to use expressions such as {@code n - (n >>> 2)} for
     * the associated resizing threshold.
     */
    //載入因子,實際上不用浮點數,而是用n - (n >>> 2)來調整閾值
    private static final float LOAD_FACTOR = 0.75f;

    /**
     * The bin count threshold for using a tree rather than list for a
     * bin.  Bins are converted to trees when adding an element to a
     * bin with at least this many nodes. The value must be greater
     * than 2, and should be at least 8 to mesh with assumptions in
     * tree removal about conversion back to plain bins upon
     * shrinkage.
     */
    //桶裡已有8個資料,再加的時候就要轉換成紅黑樹
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * The bin count threshold for untreeifying a (split) bin during a
     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
     * most 6 to mesh with shrinkage detection under removal.
     */
    //小於6時,將紅黑樹轉回連結串列
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * The smallest table capacity for which bins may be treeified.
     * (Otherwise the table is resized if too many nodes in a bin.)
     * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
     * conflicts between resizing and treeification thresholds.
     */
    //樹化所要求的陣列最小長度,要樹化陣列長度要大於此值,不滿足則呼叫tryPresize(int size)擴容
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * Minimum number of rebinnings per transfer step. Ranges are
     * subdivided to allow multiple resizer threads.  This value
     * serves as a lower bound to avoid resizers encountering
     * excessive memory contention.  The value should be at least
     * DEFAULT_CAPACITY.
     */
    private static final int MIN_TRANSFER_STRIDE = 16;

    /**
     * The number of bits used for generation stamp in sizeCtl.
     * Must be at least 6 for 32bit arrays.
     */
    //生成sizeCtl需要的位數
    private static int RESIZE_STAMP_BITS = 16;

    /**
     * The maximum number of threads that can help resize.
     * Must fit in 32 - RESIZE_STAMP_BITS bits.
     */
    //允許的進行擴容的最大程式數
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

    /**
     * The bit shift for recording size stamp in sizeCtl.
     */
    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;

    /**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    //整個雜湊表(陣列形式儲存),Node陣列
    transient volatile Node<K,V>[] table;

    /**
     * The next table to use; non-null only while resizing.
     */
    //連線表,用於雜湊表擴容,擴容完成之後重置回null
    private transient volatile Node<K,V>[] nextTable;

    /**
     * Base counter value, used mainly when there is no contention,
     * but also as a fallback during table initialization
     * races. Updated via CAS.
     */
    //儲存雜湊表中儲存所有節點個數,類似於HashMap的size
    private transient volatile long baseCount;

    /**
     * Table initialization and resizing control.  When negative, the
     * table is being initialized or resized: -1 for initialization,
     * else -(1 + the number of active resizing threads).  Otherwise,
     * when table is null, holds the initial table size to use upon
     * creation, or 0 for default. After initialization, holds the
     * next element count value upon which to resize the table.
     */
    //-1 :代表table正在初始化,其他執行緒應該交出CPU時間片
    //-N: 表示正有N-1個執行緒執行擴容操作(高 16 位是 length 生成的識別符號,低 16 位是擴容的執行緒數)
    //大於 0: 如果table已經初始化,代表table容量,預設為table大小的0.75,如果還未初始化,代表需要初始化的大小

    private transient volatile int sizeCtl;

}

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        volatile V val;
        volatile Node<K,V> next;
}

 

建構函式

     /**
     * Creates a new, empty map with the default initial table size (16).
     */
    //建立預設初始容量16,載入因子0.75,併發等級16的空對映
    public ConcurrentHashMap() {
    }

    /**
     * Creates a new, empty map with an initial table size
     * accommodating the specified number of elements without the need
     * to dynamically resize.
     *
     * @param initialCapacity The implementation performs internal
     * sizing to accommodate this many elements.
     * @throws IllegalArgumentException if the initial capacity of
     * elements is negative
     */
    //建立指定初始容量,載入因子0.75,併發等級16的空對映
    public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)    //合法性檢查
            throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                    //找到最接近initialCapacity的2次冪
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;    //sizeCtl > 0時相當於擴容閾值
    }

    /**
     * Creates a new map with the same mappings as the given map.
     *
     * @param m the map
     */
    public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
        this.sizeCtl = DEFAULT_CAPACITY;
        putAll(m);
    }

    /**
     * Creates a new, empty map with an initial table size based on
     * the given number of elements ({@code initialCapacity}) and
     * initial table density ({@code loadFactor}).
     *
     * @param initialCapacity the initial capacity. The implementation
     * performs internal sizing to accommodate this many elements,
     * given the specified load factor.
     * @param loadFactor the load factor (table density) for
     * establishing the initial table size
     * @throws IllegalArgumentException if the initial capacity of
     * elements is negative or the load factor is nonpositive
     *
     * @since 1.6
     */
    //創造指定初始容量和載入因子,以及併發等級為1的對映
    public ConcurrentHashMap(int initialCapacity, float loadFactor) {
        this(initialCapacity, loadFactor, 1);
    }

    /**
     * Creates a new, empty map with an initial table size based on
     * the given number of elements ({@code initialCapacity}), table
     * density ({@code loadFactor}), and number of concurrently
     * updating threads ({@code concurrencyLevel}).
     *
     * @param initialCapacity the initial capacity. The implementation
     * performs internal sizing to accommodate this many elements,
     * given the specified load factor.
     * @param loadFactor the load factor (table density) for
     * establishing the initial table size
     * @param concurrencyLevel the estimated number of concurrently
     * updating threads. The implementation may use this value as
     * a sizing hint.
     * @throws IllegalArgumentException if the initial capacity is
     * negative or the load factor or concurrencyLevel are
     * nonpositive
     */
    public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)    //引數合法性檢查
            throw new IllegalArgumentException();
        //有多少個桶就允許多少個併發程式
        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
            initialCapacity = concurrencyLevel;   // as estimated threads

        //size是容量,不是閾值
        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
        
        //構造時sizeCtl為2次冪(此時sizeCtl是容量,不是閾值?)
        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
        this.sizeCtl = cap;
    }

 

①public V put(K key, V value)

public V put(K key, V value) {
        return putVal(key, value, false);
    }

/** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        //ConcurrentHashMap不允許key或者value為空
        if (key == null || value == null) throw new NullPointerException();
    
        //hash的計算:return (h ^ (h >>> 16)) & HASH_BITS;
        //(h是key.hashCode()、HASH_BITS預設是0x7fffffff)
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //檢查是否需要初始化(tab為空)
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            //根據鍵的hash值找到陣列相應的index
            //如果表不為空、hash值對應的桶為空就用CAS無鎖式向該位置新增一個結點
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }

            //檢測到結點的hash值為MOVED,表示正在進行陣列擴張的資料複製階段
            //允許多執行緒複製,所以當前執行緒去幫忙複製(轉移)
            else if ((fh = f.hash) == MOVED)
                //結點轉移
                tab = helpTransfer(tab, f);

            else {
                V oldVal = null;
                //鎖住桶
                synchronized (f) {
                    //i為該桶在Node陣列的下標值,f為對應的桶
                    if (tabAt(tab, i) == f) {
                        //fh為f桶的hash值
                        if (fh >= 0) {
                            binCount = 1;
                            //遍歷f桶內所有元素
                            
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                //f桶內有key相同的鍵值對,返回一個oldVal舊值
                                //onlyIfAbsent = false(put操作),才將舊值改成新值
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                //f桶內沒有key相同鍵值對,插入一個新節點
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }

                        //如果該結點是紅黑樹
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                //返回舊值oldVal、onlyIfAbsent==false時:替換舊值
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    //連結串列深度超過8就轉換成紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }


    /**
     * Initializes table, using the size recorded in sizeCtl.
     */    
    //table為空時初始化table
    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {

            //sizeCtl < 0 代表有程式正在初始化table,讓權等待
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin

            //比較sizeCtl是否和sc相同,如果是就用-1賦值sizeCtl
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        //初始化時,sc=sizeCtl是陣列的容量而不是閾值
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        //新生節點陣列,大小為n
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        //擴容閾值設定為3n/4(也就相當於0.75 x Capacity = threshold)
                        sc = n - (n >>> 2);
                    }
                } finally {
                    //此時sizeCtl才從容量變為閾值(初始化之後)
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }


    //通過unsafe物件反射獲取桶?i為Node陣列中桶對應的下標
    @SuppressWarnings("unchecked")
    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);
    }


    //CAS操作:判斷i下標的桶是不是c,如果是就用v換c,否則不換
    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }


     


協助擴容:

/**
     * Helps transfer if a resize is in progress.
     */
    //協助擴容
    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        //由於是協助擴容,所以當前nextTable應該不為空的
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {

            //根據table長度獲得一個識別符號
            int rs = resizeStamp(tab.length);

            //如果nextTable和Table沒有被併發修改,而且處於擴容狀態
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {

                // 如果 sizeCtl 無符號右移  16 不等於 rs ( sc前 16 位如果不等於識別符號,則識別符號變化了)
                // 或者 sizeCtl == rs + 1  (擴容結束了,沒有執行緒在進行擴容)
                //(預設第一個執行緒設定 sc ==rs 左移 16 位 + 2,當第一個執行緒結束擴容了,就會將 sc 減一。這個時候,sc 就等於 rs + 1)
                // 或者 sizeCtl == rs + 65535  (如果達到最大幫助執行緒的數量,即 65535)
                // 或者轉移下標正在調整 (擴容結束)
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                    break;

                //以上情況都不是,CAS操作將sizeCtl+1,代表多了當前執行緒來幫助擴容
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                    //進行轉移
                    transfer(tab, nextTab);
                    break;

                }
            }
            return nextTab;
        }
        return table;
    }

//根據table長度獲得一個識別符號
int rs = resizeStamp(tab.length);

第一個程式在擴容時

SC的值被設定成:識別符號(高16位)+2(低16位)。也就是rs左移16位+2

當所有程式完成擴容

SC的值應該是:識別符號(高16位)+1(低16位)

 

滿足四條中 任 一 一 條 當前執行緒就不幫忙轉換結點到nextTable

  • 如果 sizeCtl 無符號右移  16 不等於 rs ( sc前 16 位如果不等於識別符號,則識別符號變化了,就代表還未擴容)
  • sizeCtl == rs + 1  (擴容結束了,沒有執行緒在進行擴容)(預設第一個執行緒設定 sc ==rs 左移 16 位 + 2,當第一個執行緒結束擴容了,就會將 sc 減一。這個時候,sc 就等於 rs + 1)
  • 或者 sizeCtl == rs + 65535  (如果達到最大幫助執行緒的數量,即 65535)
  • 或者轉移下標正在調整 (擴容結束)

                if ((sc >>> RESIZE_STAMP_SHIFT) != rs ||

                     sc == rs + 1 ||

                     sc == rs + MAX_RESIZERS ||

                     transferIndex <= 0)
                                          break;

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8214427

但是根據這裡提交的bug,

所以第二、三個判斷是沒有用的。

 

    /**
     * Moves and/or copies the nodes in each bin to new table. See
     * above for explanation.
     */
    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
        int n = tab.length, stride;
        
        //限制每個CPU處理陣列長度最少為16
        //即如果有一個陣列長度只有16,則處理此陣列的執行緒只能有一個
        //stride為每個執行緒處理的陣列長度
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; // subdivide range

        if (nextTab == null) {            // initiating
            try {
                //初始化nextTable:新建一個大小為原Table兩倍的新Table
                @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;
        }
        int nextn = nextTab.length;
        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
        boolean advance = true;
        
        //nextTable是否完成掃描的標識(false未完成、true已完成)
        boolean finishing = false; // to ensure sweep before committing nextTab
        for (int i = 0, bound = 0;;) {
            Node<K,V> f; int fh;
            while (advance) {
                int nextIndex, nextBound;
            
                //如果已經完成掃描,就不用這一步了
                if (--i >= bound || finishing)
                    advance = false;

                //需要轉換的陣列下標不合法
                else if ((nextIndex = transferIndex) <= 0) {
                    i = -1;
                    advance = false;
                }

                //CAS操作告訴執行緒要處理到哪個陣列,下標為i
                else if (U.compareAndSwapInt
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    bound = nextBound;
                    i = nextIndex - 1;
                    advance = false;
                }
            }
            if (i < 0 || i >= n || i + n >= nextn) {
                int sc;

                //完成掃描了,將舊陣列指向新陣列,清空臨時物件nextTable
                if (finishing) {
                    nextTable = null;
                    table = nextTab;
                    //擴容閾值設定成原來的1.5倍
                    sizeCtl = (n << 1) - (n >>> 1);
                    return;
                }
                
                //詳見上面關於rs和sc的分析
                //如果sc = sc - 1:代表當前執行緒執行完成,退出
                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                        return;
                    //還有第一個執行緒在擴容,就直接表示任務完成??
                    finishing = advance = true;
                    i = n; // recheck before commit
                }

            }
            //如果遍歷到的桶為空,就放入一個fwd(ForwardingNode)結點,表示這個桶已經被處理過了
            else if ((f = tabAt(tab, i)) == null)
                advance = casTabAt(tab, i, null, fwd);

            //併發控制核心:如果遍歷到fwd指標,就代表這個桶已經被處理過了,跳過。
            else if ((fh = f.hash) == MOVED)
                advance = true; // already processed 已經 處理

            //處理桶
            else {
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        Node<K,V> ln, hn;
    
                        //如果獲得的桶是連結串列結構
                        if (fh >= 0) {

                            //處理同HashMap,resize後的結點要不留在原地,要不就移動n個桶
                            //(n是原來的capacity),詳情可以往上拉看HashMap中分析
                            int runBit = fh & n;

                            Node<K,V> lastRun = f;
    
                            //遍歷連結串列結點,拿到連結串列尾的結點lastRun
                            //或者結點鏈[上面所有結點都保持原地址或者移動capacity個單位]
                            //runBit是lastRun是否移動的判斷
                            //如果要保持原序插入的話,倒過來是最好的,可以省掉一個記錄頭結點的空間
                            for (Node<K,V> p = f.next; p != null; p = p.next) {
                                int b = p.hash & n;
                                if (b != runBit) {
                                    runBit = b;
                                    lastRun = p;
                                }
                            }
                            if (runBit == 0) {
                                ln = lastRun;
                                hn = null;
                            }
                            else {
                                hn = lastRun;
                                ln = null;
                            }

                            //遍歷連結串列的每個結點根據新地址值來插入不同的連結串列
                            for (Node<K,V> p = f; p != lastRun; p = p.next) {

                                int ph = p.hash; K pk = p.key; V pv = p.val;

                                //ln = ln(new).next / hn = hn(new).next
                                //ln是resize後原地不動的連結串列
                                if ((ph & n) == 0)
                                    ln = new Node<K,V>(ph, pk, pv, ln);

                                //hn是resize後移動capacity單位的連結串列
                                else
                                    hn = new Node<K,V>(ph, pk, pv, hn);
                            }
                            setTabAt(nextTab, i, ln);
                            setTabAt(nextTab, i + n, hn);
                            setTabAt(tab, i, 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;
                                }
                            }
                            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;
                        }
                    }
                }
            }
        }
    }

 

        /**
         * Finds or adds a node.
         * @return null if added
         */
        final TreeNode<K,V> putTreeVal(int h, K k, V v) {
            Class<?> kc = null;
            boolean searched = false;
            for (TreeNode<K,V> p = root;;) {
                int dir, ph; K pk;
                if (p == null) {
                    first = root = new TreeNode<K,V>(h, k, v, null, null);
                    break;
                }
                else if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;
                else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                    return p;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) {
                    if (!searched) {
                        TreeNode<K,V> q, ch;
                        searched = true;
                        if (((ch = p.left) != null &&
                             (q = ch.findTreeNode(h, k, kc)) != null) ||
                            ((ch = p.right) != null &&
                             (q = ch.findTreeNode(h, k, kc)) != null))
                            return q;
                    }
                    dir = tieBreakOrder(k, pk);
                }

                TreeNode<K,V> xp = p;
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    TreeNode<K,V> x, f = first;
                    first = x = new TreeNode<K,V>(h, k, v, f, xp);
                    if (f != null)
                        f.prev = x;
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    if (!xp.red)
                        x.red = true;
                    else {
                        lockRoot();
                        try {
                            root = balanceInsertion(root, x);
                        } finally {
                            unlockRoot();
                        }
                    }
                    break;
                }
            }
            assert checkInvariants(root);
            return null;
        }

    /**
     * Replaces all linked nodes in bin at given index unless table is
     * too small, in which case resizes instead.
     */
    //將連結串列中的Node變為TreeNode
    private final void treeifyBin(Node<K,V>[] tab, int index) {
        Node<K,V> b; int n, sc;
        if (tab != null) {

            //判斷陣列長度是否小於最小樹化的陣列數量要求(table太小不能樹化,要先擴容)
            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;

                        //遍歷所有結點,將所有Node變為TreeNode
                        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);
                            //只是用TreeNode封裝,沒有用到left/right/parent等屬性
                            if ((p.prev = tl) == null)
                                hd = p;
                            else
                                tl.next = p;
                            tl = p;
                        }
                        setTabAt(tab, index, new TreeBin<K,V>(hd));
                    }
                }
            }
        }
    }

putVal()---addCount(1L, binCount);

/**
     * Adds to count, and if table is too small and not already
     * resizing, initiates transfer. If already resizing, helps
     * perform transfer if work is available.  Rechecks occupancy
     * after a transfer to see if another resize is already needed
     * because resizings are lagging additions.
     *
     * @param x the count to add
     * @param check if <0, don't check resize, if <= 1 only check if uncontended
     */
    //x是要加的個數
    //check當前桶已有結點個數,check<0就不用檢查是否需要擴容;check<=1就只檢查是否沒有併發
    private final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        
        //counterCells用於併發新增數量(有這個物件就代表併發了;沒有不代表沒有併發,還未處理)
        //baseCount用於無競爭新增數量(CAS成功代表沒有併發)
        if ((as = counterCells) != null ||
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            CounterCell a; long v; int m;
            boolean uncontended = true;

            //as == null :併發已發生,尚未處理併發
            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();
        }
        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();
            }
        }
    }

 

 

ConcurrentSkipListMap

 

相關文章