HashMap1.8和ConcurentHashMap1.8原理分析

jdk1.8之后對(duì)HashMap做了很大的優(yōu)化力奋,把原有的(數(shù)組+鏈表)結(jié)構(gòu)改成了(數(shù)組+鏈表+紅黑樹)凡资。
當(dāng)鏈表的長度大于8的時(shí)候渴肉,自動(dòng)轉(zhuǎn)換成紅黑樹冗懦。把原來的Entry改為節(jié)點(diǎn)Node。jdk1.8中HashMap的數(shù)據(jù)結(jié)構(gòu)如下:


image.png

基本屬性

/**
 * The default initial capacity - MUST be a power of two.
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默認(rèn)容量16
 
/**
 * The maximum capacity, used if a higher value is implicitly specified
 * by either of the constructors with arguments.
 * MUST be a power of two <= 1<<30.
 */
static final int MAXIMUM_CAPACITY = 1 << 30;    // 最大容量
 
/**
 * The load factor used when none specified in constructor.
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默認(rèn)負(fù)載因子0.75
 
/**
 * 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.
 */
static final int TREEIFY_THRESHOLD = 8; // 鏈表節(jié)點(diǎn)轉(zhuǎn)換紅黑樹節(jié)點(diǎn)的閾值, 9個(gè)節(jié)點(diǎn)轉(zhuǎn)
 
/**
 * 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.
 */
static final int UNTREEIFY_THRESHOLD = 6;   // 紅黑樹節(jié)點(diǎn)轉(zhuǎn)換鏈表節(jié)點(diǎn)的閾值, 6個(gè)節(jié)點(diǎn)轉(zhuǎn)
 
/**
 * The smallest table capacity for which bins may be treeified.
 * (Otherwise the table is resized if too many nodes in a bin.)
 * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
 * between resizing and treeification thresholds.
 */
static final int MIN_TREEIFY_CAPACITY = 64; // 轉(zhuǎn)紅黑樹時(shí), table的最小長度
 
/**
 * Basic hash bin node, used for most entries.  (See below for
 * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
 */
static class Node<K,V> implements Map.Entry<K,V> {  // 基本hash節(jié)點(diǎn), 繼承自Entry
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
 
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
 
    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }
 
    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }
 
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
 
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}
 
/**
 * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
 * extends Node) so can be used as extension of either regular or
 * linked node.
 */
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {// 紅黑樹節(jié)點(diǎn)
    TreeNode<K,V> parent;  // red-black tree links
    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) {
        super(hash, key, val, next);
    }
    // ...
}

定位節(jié)點(diǎn)下標(biāo)

計(jì)算hash值的運(yùn)算比1.7的更簡單仇祭。計(jì)算節(jié)點(diǎn)下標(biāo)和1.7的算法是一樣的披蕉,這里就不詳細(xì)描述,具體請(qǐng)看我的上一篇文章:http://www.reibang.com/p/2d8647961ab5

// 代碼1
static final int hash(Object key) { // 計(jì)算key的hash值
    int h;
    // 1.先拿到key的hashCode值; 2.將hashCode的高16位參與運(yùn)算
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// 代碼2
int n = tab.length;
// 將(tab.length - 1) 與 hash值進(jìn)行&運(yùn)算
int index = (n - 1) & hash;

get方法

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;
    // table不為空 && table長度大于0 && table索引位置(根據(jù)hash值計(jì)算出)不為空
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (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;   // first的key等于傳入的key則返回first對(duì)象
        if ((e = first.next) != null) { // 向下遍歷
            if (first instanceof TreeNode)  // 判斷是否為TreeNode
                // 如果是紅黑樹節(jié)點(diǎn)乌奇,則調(diào)用紅黑樹的查找目標(biāo)節(jié)點(diǎn)方法getTreeNode
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            // 走到這代表節(jié)點(diǎn)為鏈表節(jié)點(diǎn)
            do { // 向下遍歷鏈表, 直至找到節(jié)點(diǎn)的key和傳入的key相等時(shí),返回該節(jié)點(diǎn)
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;    // 找不到符合的返回空
}

1没讲,先判斷table不為空,且table的長度大于0华弓,且table索引位置不為空
2食零,檢查第一個(gè)節(jié)點(diǎn)困乒,如果第一個(gè)節(jié)點(diǎn)就是要的值寂屏,直接返回。
3娜搂,向下遍歷鏈表迁霎,判斷是否為treeNode,如果是就調(diào)用getTreeNode
4,如果不是紅黑樹百宇,就遍歷鏈表找到數(shù)據(jù)
5考廉,如果都找不到直接返回null
接下來看getTreeNode

final TreeNode<K,V> getTreeNode(int h, Object k) {
    // 使用根結(jié)點(diǎn)調(diào)用find方法   
    // 查找根節(jié)點(diǎn), 索引位置的頭節(jié)點(diǎn)并不一定為紅黑樹的根結(jié)點(diǎn)
    return ((parent != null) ? root() : this).find(h, k, null); 
}

/**
 * 從調(diào)用此方法的結(jié)點(diǎn)開始查找, 通過hash值和key找到對(duì)應(yīng)的節(jié)點(diǎn)
 * 此處是紅黑樹的遍歷, 紅黑樹是特殊的自平衡二叉查找樹
 * 平衡二叉查找樹的特點(diǎn):左節(jié)點(diǎn)<根節(jié)點(diǎn)<右節(jié)點(diǎn)
 */
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {    
    TreeNode<K,V> p = this; // this為調(diào)用此方法的節(jié)點(diǎn)
    do {
        int ph, dir; K pk;
        TreeNode<K,V> pl = p.left, pr = p.right, q;
        if ((ph = p.hash) > h)  // 傳入的hash值小于p節(jié)點(diǎn)的hash值, 則往p節(jié)點(diǎn)的左邊遍歷
            p = pl; // p賦值為p節(jié)點(diǎn)的左節(jié)點(diǎn)
        else if (ph < h)    // 傳入的hash值大于p節(jié)點(diǎn)的hash值, 則往p節(jié)點(diǎn)的右邊遍歷
            p = pr; // p賦值為p節(jié)點(diǎn)的右節(jié)點(diǎn)
        // 傳入的hash值和key值等于p節(jié)點(diǎn)的hash值和key值,則p節(jié)點(diǎn)為目標(biāo)節(jié)點(diǎn),返回p節(jié)點(diǎn)
        else if ((pk = p.key) == k || (k != null && k.equals(pk))) 
            return p;
        else if (pl == null)    // p節(jié)點(diǎn)的左節(jié)點(diǎn)為空則將向右遍歷
            p = pr; 
        else if (pr == null)    // p節(jié)點(diǎn)的右節(jié)點(diǎn)為空則向左遍歷
            p = pl;
        else if ((kc != null ||
                 // 如果傳入的key(k)所屬的類實(shí)現(xiàn)了Comparable接口,則將傳入的key跟p節(jié)點(diǎn)的key比較
                  (kc = comparableClassFor(k)) != null) && // 此行不為空代表k實(shí)現(xiàn)了Comparable
                 (dir = compareComparables(kc, k, pk)) != 0)//k<pk則dir<0, k>pk則dir>0
            p = (dir < 0) ? pl : pr;    // k < pk則向左遍歷(p賦值為p的左節(jié)點(diǎn)), 否則向右遍歷
        // 代碼走到此處, 代表key所屬類沒有實(shí)現(xiàn)Comparable, 直接指定向p的右邊遍歷
        else if ((q = pr.find(h, k, kc)) != null)   
            return q;
        else// 代碼走到此處代表上一個(gè)向右遍歷(pr.find(h, k, kc))為空, 因此直接向左遍歷
            p = pl; 
    } while (p != null);
    return null;
}

1,將p節(jié)點(diǎn)賦值為調(diào)用此方法的節(jié)點(diǎn)
2携御,如果傳入的hash值小于p節(jié)點(diǎn)的hash值昌粤,則往p節(jié)點(diǎn)的左邊遍歷
3,如果傳入的hash值大于p節(jié)點(diǎn)的hash值啄刹,則往p節(jié)點(diǎn)的右邊遍歷
4涮坐,如果傳入的hash值等于p節(jié)點(diǎn)的hash值,并且傳入的key值跟p節(jié)點(diǎn)的key值相等, 則該p節(jié)點(diǎn)即為目標(biāo)節(jié)點(diǎn)誓军,返回p節(jié)點(diǎn)
5袱讹,如果p的左節(jié)點(diǎn)為空則向右遍歷,反之如果p的右節(jié)點(diǎn)為空則向左遍歷
6昵时,如果傳入的key(即代碼中的參數(shù)變量k)所屬的類實(shí)現(xiàn)了Comparable接口(kc不為空捷雕,comparableClassFor方法見下文代碼塊3),則將傳入的key跟p節(jié)點(diǎn)的key進(jìn)行比較(kc實(shí)現(xiàn)了Comparable接口壹甥,因此通過kc的比較方法進(jìn)行比較)救巷,并將比較結(jié)果賦值給dir,如果dir<0則代表k<pk句柠,則向p節(jié)點(diǎn)的左邊遍歷(pl)征绸;否則久橙,向p節(jié)點(diǎn)的右邊遍歷(pr)。
7管怠,代碼走到此處淆衷,代表key所屬類沒有實(shí)現(xiàn)Comparable,因此直接指定向p的右邊遍歷渤弛,如果能找到目標(biāo)節(jié)點(diǎn)則返回
8祝拯,代碼走到此處代表與第7點(diǎn)向右遍歷沒有找到目標(biāo)節(jié)點(diǎn),因此直接向左邊遍歷
9她肯,以上都找不到目標(biāo)節(jié)點(diǎn)則返回空

/**
 * Returns x's Class if it is of the form "class C implements
 * Comparable<C>", else null.
 */
static Class<?> comparableClassFor(Object x) {
    if (x instanceof Comparable) {
        Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
        if ((c = x.getClass()) == String.class) // bypass checks
            return c;
        if ((ts = c.getGenericInterfaces()) != null) {
            for (int i = 0; i < ts.length; ++i) {
                if (((t = ts[i]) instanceof ParameterizedType) &&
                    ((p = (ParameterizedType)t).getRawType() ==
                     Comparable.class) &&
                    (as = p.getActualTypeArguments()) != null &&
                    as.length == 1 && as[0] == c) // type arg is c
                    return c;
            }
        }
    }
    return null;
}

如果x實(shí)現(xiàn)了Comparable接口佳头,則返回 x的Class。

put方法

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
 
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是否為空或者length等于0, 如果是則調(diào)用resize方法進(jìn)行初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;    
    // 通過hash值計(jì)算索引位置, 如果table表該索引位置節(jié)點(diǎn)為空則新增一個(gè)
    if ((p = tab[i = (n - 1) & hash]) == null)// 將索引位置的頭節(jié)點(diǎn)賦值給p
        tab[i] = newNode(hash, key, value, null);
    else {  // table表該索引位置不為空
        Node<K,V> e; K k;
        if (p.hash == hash && // 判斷p節(jié)點(diǎn)的hash值和key值是否跟傳入的hash值和key值相等
            ((k = p.key) == key || (key != null && key.equals(k)))) 
            e = p;  // 如果相等, 則p節(jié)點(diǎn)即為要查找的目標(biāo)節(jié)點(diǎn)晴氨,賦值給e
        // 判斷p節(jié)點(diǎn)是否為TreeNode, 如果是則調(diào)用紅黑樹的putTreeVal方法查找目標(biāo)節(jié)點(diǎn)
        else if (p instanceof TreeNode) 
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {  // 走到這代表p節(jié)點(diǎn)為普通鏈表節(jié)點(diǎn)
            for (int binCount = 0; ; ++binCount) {  // 遍歷此鏈表, binCount用于統(tǒng)計(jì)節(jié)點(diǎn)數(shù)
                if ((e = p.next) == null) { // p.next為空代表不存在目標(biāo)節(jié)點(diǎn)則新增一個(gè)節(jié)點(diǎn)插入鏈表尾部
                    p.next = newNode(hash, key, value, null);
                    // 計(jì)算節(jié)點(diǎn)是否超過8個(gè), 減一是因?yàn)檠h(huán)是從p節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)開始的
                    if (binCount >= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);// 如果超過8個(gè)康嘉,調(diào)用treeifyBin方法將該鏈表轉(zhuǎn)換為紅黑樹
                    break;
                }
                if (e.hash == hash && // e節(jié)點(diǎn)的hash值和key值都與傳入的相等, 則e即為目標(biāo)節(jié)點(diǎn),跳出循環(huán)
                    ((k = e.key) == key || (key != null && key.equals(k)))) 
                    break;
                p = e;  // 將p指向下一個(gè)節(jié)點(diǎn)
            }
        }
        // e不為空則代表根據(jù)傳入的hash值和key值查找到了節(jié)點(diǎn),將該節(jié)點(diǎn)的value覆蓋,返回oldValue
        if (e != null) { 
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e); // 用于LinkedHashMap
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold) // 插入節(jié)點(diǎn)后超過閾值則進(jìn)行擴(kuò)容
        resize();
    afterNodeInsertion(evict);  // 用于LinkedHashMap
    return null;
}

1,校驗(yàn)table是否為空或者length等于0籽前,如果是則調(diào)用resize方法(見下文resize方法)進(jìn)行初始化
2亭珍,通過hash值計(jì)算索引位置,將該索引位置的頭節(jié)點(diǎn)賦值給p節(jié)點(diǎn)枝哄,如果該索引位置節(jié)點(diǎn)為空則使用傳入的參數(shù)新增一個(gè)節(jié)點(diǎn)并放在該索引位置
3肄梨,判斷p節(jié)點(diǎn)的key和hash值是否跟傳入的相等,如果相等, 則p節(jié)點(diǎn)即為要查找的目標(biāo)節(jié)點(diǎn)挠锥,將p節(jié)點(diǎn)賦值給e節(jié)點(diǎn)
4众羡,如果p節(jié)點(diǎn)不是目標(biāo)節(jié)點(diǎn),則判斷p節(jié)點(diǎn)是否為TreeNode蓖租,如果是則調(diào)用紅黑樹的putTreeVal方法(見下文代碼塊4)查找目標(biāo)節(jié)點(diǎn)
5粱侣,走到這代表p節(jié)點(diǎn)為普通鏈表節(jié)點(diǎn),則調(diào)用普通的鏈表方法進(jìn)行查找蓖宦,并定義變量binCount來統(tǒng)計(jì)該鏈表的節(jié)點(diǎn)數(shù)
6齐婴,如果p的next節(jié)點(diǎn)為空時(shí),則代表找不到目標(biāo)節(jié)點(diǎn)球昨,則新增一個(gè)節(jié)點(diǎn)并插入鏈表尾部尔店,并校驗(yàn)節(jié)點(diǎn)數(shù)是否超過8個(gè),如果超過則調(diào)用treeifyBin方法(見下文代碼塊6)將鏈表節(jié)點(diǎn)轉(zhuǎn)為紅黑樹節(jié)點(diǎn)
7主慰,如果遍歷的e節(jié)點(diǎn)存在hash值和key值都與傳入的相同嚣州,則e節(jié)點(diǎn)即為目標(biāo)節(jié)點(diǎn),跳出循環(huán)
8共螺,如果e節(jié)點(diǎn)不為空该肴,則代表目標(biāo)節(jié)點(diǎn)存在,使用傳入的value覆蓋該節(jié)點(diǎn)的value藐不,并返回oldValue
9匀哄,如果插入節(jié)點(diǎn)后節(jié)點(diǎn)數(shù)超過閾值秦效,則調(diào)用resize方法(見下文resize方法)進(jìn)行擴(kuò)容
繼續(xù)看putTreeVal方法

/**
 * Tree version of putVal.
 * 紅黑樹插入會(huì)同時(shí)維護(hù)原來的鏈表屬性, 即原來的next屬性
 */
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;
    // 查找根節(jié)點(diǎn), 索引位置的頭節(jié)點(diǎn)并不一定為紅黑樹的根結(jié)點(diǎn)
    TreeNode<K,V> root = (parent != null) ? root() : this;  
    for (TreeNode<K,V> p = root;;) {    // 將根節(jié)點(diǎn)賦值給p, 開始遍歷
        int dir, ph; K pk;
        if ((ph = p.hash) > h)  // 如果傳入的hash值小于p節(jié)點(diǎn)的hash值 
            dir = -1;   // 則將dir賦值為-1, 代表向p的左邊查找樹
        else if (ph < h)    // 如果傳入的hash值大于p節(jié)點(diǎn)的hash值,
            dir = 1;    // 則將dir賦值為1, 代表向p的右邊查找樹
        // 如果傳入的hash值和key值等于p節(jié)點(diǎn)的hash值和key值, 則p節(jié)點(diǎn)即為目標(biāo)節(jié)點(diǎn), 返回p節(jié)點(diǎn)
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))  
            return p;
        // 如果k所屬的類沒有實(shí)現(xiàn)Comparable接口 或者 k和p節(jié)點(diǎn)的key相等
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) { 
            if (!searched) {    // 第一次符合條件, 該方法只有第一次才執(zhí)行
                TreeNode<K,V> q, ch;
                searched = true;
                // 從p節(jié)點(diǎn)的左節(jié)點(diǎn)和右節(jié)點(diǎn)分別調(diào)用find方法進(jìn)行查找, 如果查找到目標(biāo)節(jié)點(diǎn)則返回
                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;
            }
            // 否則使用定義的一套規(guī)則來比較k和p節(jié)點(diǎn)的key的大小, 用來決定向左還是向右查找
            dir = tieBreakOrder(k, pk); // dir<0則代表k<pk,則向p左邊查找涎嚼;反之亦然
        }
 
        TreeNode<K,V> xp = p;   // xp賦值為x的父節(jié)點(diǎn),中間變量,用于下面給x的父節(jié)點(diǎn)賦值
        // dir<=0則向p左邊查找,否則向p右邊查找,如果為null,則代表該位置即為x的目標(biāo)位置
        if ((p = (dir <= 0) ? p.left : p.right) == null) {  
            // 走進(jìn)來代表已經(jīng)找到x的位置阱州,只需將x放到該位置即可
            Node<K,V> xpn = xp.next;    // xp的next節(jié)點(diǎn)      
            // 創(chuàng)建新的節(jié)點(diǎn), 其中x的next節(jié)點(diǎn)為xpn, 即將x節(jié)點(diǎn)插入xp與xpn之間
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);   
            if (dir <= 0)   // 如果時(shí)dir <= 0, 則代表x節(jié)點(diǎn)為xp的左節(jié)點(diǎn)
                xp.left = x;
            else        // 如果時(shí)dir> 0, 則代表x節(jié)點(diǎn)為xp的右節(jié)點(diǎn)
                xp.right = x;
            xp.next = x;    // 將xp的next節(jié)點(diǎn)設(shè)置為x
            x.parent = x.prev = xp; // 將x的parent和prev節(jié)點(diǎn)設(shè)置為xp
            // 如果xpn不為空,則將xpn的prev節(jié)點(diǎn)設(shè)置為x節(jié)點(diǎn),與上文的x節(jié)點(diǎn)的next節(jié)點(diǎn)對(duì)應(yīng)
            if (xpn != null)    
                ((TreeNode<K,V>)xpn).prev = x;
            moveRootToFront(tab, balanceInsertion(root, x)); // 進(jìn)行紅黑樹的插入平衡調(diào)整
            return null;
        }
    }
}

1,查找當(dāng)前紅黑樹的根結(jié)點(diǎn)法梯,將根結(jié)點(diǎn)賦值給p節(jié)點(diǎn)苔货,開始進(jìn)行查找
2,如果傳入的hash值小于p節(jié)點(diǎn)的hash值立哑,將dir賦值為-1夜惭,代表向p的左邊查找樹
3,如果傳入的hash值大于p節(jié)點(diǎn)的hash值铛绰, 將dir賦值為1诈茧,代表向p的右邊查找樹
4,如果傳入的hash值等于p節(jié)點(diǎn)的hash值捂掰,并且傳入的key值跟p節(jié)點(diǎn)的key值相等, 則該p節(jié)點(diǎn)即為目標(biāo)節(jié)點(diǎn)敢会,返回p節(jié)點(diǎn)
5,如果k所屬的類沒有實(shí)現(xiàn)Comparable接口尘颓,或者k和p節(jié)點(diǎn)的key使用compareTo方法比較相等:第一次會(huì)從p節(jié)點(diǎn)的左節(jié)點(diǎn)和右節(jié)點(diǎn)分別調(diào)用find方法(見上文代碼塊2)進(jìn)行查找走触,如果查找到目標(biāo)節(jié)點(diǎn)則返回晦譬;如果不是第一次或者調(diào)用find方法沒有找到目標(biāo)節(jié)點(diǎn)疤苹,則調(diào)用tieBreakOrder方法(見下文代碼塊5)比較k和p節(jié)點(diǎn)的key值的大小,以決定向樹的左節(jié)點(diǎn)還是右節(jié)點(diǎn)查找敛腌。
6卧土,如果dir <= 0則向左節(jié)點(diǎn)查找(p賦值為p.left,并進(jìn)行下一次循環(huán))像樊,否則向右節(jié)點(diǎn)查找尤莺,如果已經(jīng)無法繼續(xù)查找(p賦值后為null),則代表該位置即為x的目標(biāo)位置生棍,另外變量xp用來記錄查找的最后一個(gè)節(jié)點(diǎn)颤霎,即下文新增的x節(jié)點(diǎn)的父節(jié)點(diǎn)。
7涂滴,以傳入的hash友酱、key、value參數(shù)和xp節(jié)點(diǎn)的next節(jié)點(diǎn)為參數(shù)柔纵,構(gòu)建x節(jié)點(diǎn)(注意:xp節(jié)點(diǎn)在此處可能是葉子節(jié)點(diǎn)缔杉、沒有左節(jié)點(diǎn)的節(jié)點(diǎn)、沒有右節(jié)點(diǎn)的節(jié)點(diǎn)三種情況搁料,即使它是葉子節(jié)點(diǎn)或详,它也可能有next節(jié)點(diǎn)系羞,紅黑樹的結(jié)構(gòu)跟鏈表的結(jié)構(gòu)是互不影響的,不會(huì)因?yàn)槟硞€(gè)節(jié)點(diǎn)是葉子節(jié)點(diǎn)就說它沒有next節(jié)點(diǎn)霸琴,紅黑樹在進(jìn)行操作時(shí)會(huì)同時(shí)維護(hù)紅黑樹結(jié)構(gòu)和鏈表結(jié)構(gòu)椒振,next屬性就是用來維護(hù)鏈表結(jié)構(gòu)的),根據(jù)dir的值決定x決定放在xp節(jié)點(diǎn)的左節(jié)點(diǎn)還是右節(jié)點(diǎn)梧乘,將xp的next節(jié)點(diǎn)設(shè)為x杠人,將x的parent和prev節(jié)點(diǎn)設(shè)為xp,如果原xp的next節(jié)點(diǎn)(xpn)不為空, 則將該節(jié)點(diǎn)的prev節(jié)點(diǎn)設(shè)置為x節(jié)點(diǎn), 與上面的將x節(jié)點(diǎn)的next節(jié)點(diǎn)設(shè)置為xpn對(duì)應(yīng)宋下。
8嗡善,進(jìn)行紅黑樹的插入平衡調(diào)整,見文末的解釋2学歧。
接著查看treeifyBin,將鏈表轉(zhuǎn)換為紅黑樹

final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    // table為空或者table的長度小于64, 進(jìn)行擴(kuò)容
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) 
        resize();
    // 根據(jù)hash值計(jì)算索引值, 遍歷該索引位置的鏈表
    else if ((e = tab[index = (n - 1) & hash]) != null) {   
        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null); // 鏈表節(jié)點(diǎn)轉(zhuǎn)紅黑樹節(jié)點(diǎn)
            if (tl == null) // tl為空代表為第一次循環(huán)
                hd = p; // 頭結(jié)點(diǎn)
            else {
                p.prev = tl;    // 當(dāng)前節(jié)點(diǎn)的prev屬性設(shè)為上一個(gè)節(jié)點(diǎn)
                tl.next = p;    // 上一個(gè)節(jié)點(diǎn)的next屬性設(shè)置為當(dāng)前節(jié)點(diǎn)
            }
            tl = p; // tl賦值為p, 在下一次循環(huán)中作為上一個(gè)節(jié)點(diǎn)
        } while ((e = e.next) != null); // e指向下一個(gè)節(jié)點(diǎn)
        // 將table該索引位置賦值為新轉(zhuǎn)的TreeNode的頭節(jié)點(diǎn)
        if ((tab[index] = hd) != null) 
            hd.treeify(tab);    // 以頭結(jié)點(diǎn)為根結(jié)點(diǎn), 構(gòu)建紅黑樹
    }
}

1罩引,校驗(yàn)table是否為空,如果長度小于64枝笨,則調(diào)用resize方法(見下文resize方法)進(jìn)行擴(kuò)容袁铐。
2,根據(jù)hash值計(jì)算索引值横浑,將該索引位置的節(jié)點(diǎn)賦值給e節(jié)點(diǎn)剔桨,從e節(jié)點(diǎn)開始遍歷該索引位置的鏈表。
3徙融,調(diào)用replacementTreeNode方法(該方法就一行代碼洒缀,直接返回一個(gè)新建的TreeNode)將鏈表節(jié)點(diǎn)轉(zhuǎn)為紅黑樹節(jié)點(diǎn),將頭結(jié)點(diǎn)賦值給hd節(jié)點(diǎn)欺冀,每次遍歷結(jié)束將p節(jié)點(diǎn)賦值給tl树绩,用于在下一次循環(huán)中作為上一個(gè)節(jié)點(diǎn)進(jìn)行一些鏈表的關(guān)聯(lián)操作(p.prev = tl 和 tl.next = p)。
4隐轩,將table該索引位置賦值為新轉(zhuǎn)的TreeNode的頭節(jié)點(diǎn)hd饺饭,如果該節(jié)點(diǎn)不為空,則以hd為根結(jié)點(diǎn)职车,調(diào)用treeify方法構(gòu)建紅黑樹瘫俊。

final void treeify(Node<K,V>[] tab) {   // 構(gòu)建紅黑樹
    TreeNode<K,V> root = null;
    for (TreeNode<K,V> x = this, next; x != null; x = next) {// this即為調(diào)用此方法的TreeNode
        next = (TreeNode<K,V>)x.next;   // next賦值為x的下個(gè)節(jié)點(diǎn)
        x.left = x.right = null;    // 將x的左右節(jié)點(diǎn)設(shè)置為空
        if (root == null) { // 如果還沒有根結(jié)點(diǎn), 則將x設(shè)置為根結(jié)點(diǎn)
            x.parent = null;    // 根結(jié)點(diǎn)沒有父節(jié)點(diǎn)
            x.red = false;  // 根結(jié)點(diǎn)必須為黑色
            root = x;   // 將x設(shè)置為根結(jié)點(diǎn)
        }
        else {
            K k = x.key;    // k賦值為x的key
            int h = x.hash; // h賦值為x的hash值
            Class<?> kc = null;
            // 如果當(dāng)前節(jié)點(diǎn)x不是根結(jié)點(diǎn), 則從根節(jié)點(diǎn)開始查找屬于該節(jié)點(diǎn)的位置
            for (TreeNode<K,V> p = root;;) {    
                int dir, ph;
                K pk = p.key;   
                if ((ph = p.hash) > h)  // 如果x節(jié)點(diǎn)的hash值小于p節(jié)點(diǎn)的hash值
                    dir = -1;   // 則將dir賦值為-1, 代表向p的左邊查找
                else if (ph < h)    // 與上面相反, 如果x節(jié)點(diǎn)的hash值大于p節(jié)點(diǎn)的hash值
                    dir = 1;    // 則將dir賦值為1, 代表向p的右邊查找
                // 走到這代表x的hash值和p的hash值相等,則比較key值
                else if ((kc == null && // 如果k沒有實(shí)現(xiàn)Comparable接口 或者 x節(jié)點(diǎn)的key和p節(jié)點(diǎn)的key相等
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0)
                    // 使用定義的一套規(guī)則來比較x節(jié)點(diǎn)和p節(jié)點(diǎn)的大小悴灵,用來決定向左還是向右查找
                    dir = tieBreakOrder(k, pk); 
 
                TreeNode<K,V> xp = p;   // xp賦值為x的父節(jié)點(diǎn),中間變量用于下面給x的父節(jié)點(diǎn)賦值
                // dir<=0則向p左邊查找,否則向p右邊查找,如果為null,則代表該位置即為x的目標(biāo)位置
                if ((p = (dir <= 0) ? p.left : p.right) == null) { 
                    x.parent = xp;  // x的父節(jié)點(diǎn)即為最后一次遍歷的p節(jié)點(diǎn)
                    if (dir <= 0)   // 如果時(shí)dir <= 0, 則代表x節(jié)點(diǎn)為父節(jié)點(diǎn)的左節(jié)點(diǎn)
                        xp.left = x;
                    else    // 如果時(shí)dir > 0, 則代表x節(jié)點(diǎn)為父節(jié)點(diǎn)的右節(jié)點(diǎn)
                        xp.right = x;
                    // 進(jìn)行紅黑樹的插入平衡(通過左旋扛芽、右旋和改變節(jié)點(diǎn)顏色來保證當(dāng)前樹符合紅黑樹的要求)
                    root = balanceInsertion(root, x);   
                    break;
                }
            }
        }
    }
    moveRootToFront(tab, root); // 如果root節(jié)點(diǎn)不在table索引位置的頭結(jié)點(diǎn), 則將其調(diào)整為頭結(jié)點(diǎn)
}
/**
 * 如果當(dāng)前索引位置的頭節(jié)點(diǎn)不是root節(jié)點(diǎn), 則將root的上一個(gè)節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)進(jìn)行關(guān)聯(lián), 
 * 將root放到頭節(jié)點(diǎn)的位置, 原頭節(jié)點(diǎn)放在root的next節(jié)點(diǎn)上
 */
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) {    // 如果root節(jié)點(diǎn)不是該索引位置的頭節(jié)點(diǎn)
            Node<K,V> rn;
            tab[index] = root;  // 將該索引位置的頭節(jié)點(diǎn)賦值為root節(jié)點(diǎn)
            TreeNode<K,V> rp = root.prev;   // root節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)
            // 如果root節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為空, 
            // 則將root節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)的prev屬性設(shè)置為root節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)
            if ((rn = root.next) != null)   
                ((TreeNode<K,V>)rn).prev = rp; 
            // 如果root節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)不為空, 
            // 則將root節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)的next屬性設(shè)置為root節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
            if (rp != null) 
                rp.next = rn;
            if (first != null)  // 如果原頭節(jié)點(diǎn)不為空, 則將原頭節(jié)點(diǎn)的prev屬性設(shè)置為root節(jié)點(diǎn)
                first.prev = root;
            root.next = first;  // 將root節(jié)點(diǎn)的next屬性設(shè)置為原頭節(jié)點(diǎn)
            root.prev = null;
        }
        assert checkInvariants(root);   // 檢查樹是否正常
    }
}

resize

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {   // 老table不為空
        if (oldCap >= MAXIMUM_CAPACITY) {      // 老table的容量超過最大容量值
            threshold = Integer.MAX_VALUE;  // 設(shè)置閾值為Integer.MAX_VALUE
            return oldTab;
        }
        // 將新容量賦值為老容量*2,如果新容量<最大容量并且老容量>=16, 則將新閾值設(shè)置為原來的兩倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)   
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // 老表的容量為0, 老表的閾值大于0, 是因?yàn)槌跏既萘勘环湃腴撝?        newCap = oldThr;    // 則將新表的容量設(shè)置為老表的閾值 
    else {  // 老表的容量為0, 老表的閾值為0, 則為空表称勋,設(shè)置默認(rèn)容量和閾值
        newCap = DEFAULT_INITIAL_CAPACITY; 
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {  // 如果新閾值為空, 則通過新的容量*負(fù)載因子獲得新閾值
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr; // 將當(dāng)前閾值賦值為剛計(jì)算出來的新的閾值
    @SuppressWarnings({"rawtypes","unchecked"})
    // 定義新表,容量為剛計(jì)算出來的新容量
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab; // 將當(dāng)前的表賦值為新定義的表
    if (oldTab != null) {   // 如果老表不為空, 則需遍歷將節(jié)點(diǎn)賦值給新表
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {  // 將索引值為j的老表頭節(jié)點(diǎn)賦值給e
                oldTab[j] = null; // 將老表的節(jié)點(diǎn)設(shè)置為空, 以便垃圾收集器回收空間
                // 如果e.next為空, 則代表老表的該位置只有1個(gè)節(jié)點(diǎn), 
                // 通過hash值計(jì)算新表的索引位置, 直接將該節(jié)點(diǎn)放在該位置
                if (e.next == null) 
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                     // 調(diào)用treeNode的hash分布(跟下面最后一個(gè)else的內(nèi)容幾乎相同)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null; // 存儲(chǔ)跟原索引位置相同的節(jié)點(diǎn)
                    Node<K,V> hiHead = null, hiTail = null; // 存儲(chǔ)索引位置為:原索引+oldCap的節(jié)點(diǎn)
                    Node<K,V> next;
                    do {
                        next = e.next;
                        //如果e的hash值與老表的容量進(jìn)行與運(yùn)算為0,則擴(kuò)容后的索引位置跟老表的索引位置一樣
                        if ((e.hash & oldCap) == 0) {   
                            if (loTail == null) // 如果loTail為空, 代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn)
                                loHead = e; // 則將loHead賦值為第一個(gè)節(jié)點(diǎn)
                            else    
                                loTail.next = e;    // 否則將節(jié)點(diǎn)添加在loTail后面
                            loTail = e; // 并將loTail賦值為新增的節(jié)點(diǎn)
                        }
                        //如果e的hash值與老表的容量進(jìn)行與運(yùn)算為1,則擴(kuò)容后的索引位置為:老表的索引位置+oldCap
                        else {  
                            if (hiTail == null) // 如果hiTail為空, 代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn)
                                hiHead = e; // 則將hiHead賦值為第一個(gè)節(jié)點(diǎn)
                            else
                                hiTail.next = e;    // 否則將節(jié)點(diǎn)添加在hiTail后面
                            hiTail = e; // 并將hiTail賦值為新增的節(jié)點(diǎn)
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null; // 最后一個(gè)節(jié)點(diǎn)的next設(shè)為空
                        newTab[j] = loHead; // 將原索引位置的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)
                    }
                    if (hiTail != null) {
                        hiTail.next = null; // 最后一個(gè)節(jié)點(diǎn)的next設(shè)為空
                        newTab[j + oldCap] = hiHead; // 將索引位置為原索引+oldCap的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)
                    }
                }
            }
        }
    }
    return newTab;
}

1胸哥,如果老表的容量大于0,判斷老表的容量是否超過最大容量值:如果超過則將閾值設(shè)置為Integer.MAX_VALUE赡鲜,并直接返回老表(此時(shí)oldCap * 2比Integer.MAX_VALUE大空厌,因此無法進(jìn)行重新分布庐船,只是單純的將閾值擴(kuò)容到最大);將新表的容量賦值為老表的容量*2嘲更,如果新容量小于最大容量并且老容量不小于16筐钟,則直接將新的閾值設(shè)置為原來的兩倍。
2赋朦,如果老表的容量為0篓冲,老表的閾值大于0,這種情況是傳了容量的new方法創(chuàng)建的空表宠哄,將新表的容量設(shè)置為老表的閾值(這種情況發(fā)生在新創(chuàng)建的HashMap第一次put時(shí)壹将,該HashMap初始化的時(shí)候傳了初始容量,由于HashMap并沒有capacity變量來存放容量值毛嫉,因此傳進(jìn)來的初始容量是存放在threshold變量上(查看HashMap(int initialCapacity, float loadFactor)方法)诽俯,因此此時(shí)老表的threshold的值就是我們要新創(chuàng)建的HashMap的capacity,所以將新表的容量設(shè)置為老表的閾值承粤。
3暴区,如果老表的容量為0,老表的閾值為0辛臊,這種情況是沒有傳容量的new方法創(chuàng)建的空表仙粱,將閾值和容量設(shè)置為默認(rèn)值。
4彻舰,如果新表的閾值為空伐割,則通過新的容量 * 負(fù)載因子獲得閾值(這種情況是初始化的時(shí)候傳了初始容量,跟第2點(diǎn)相同情況淹遵,或者初始容量設(shè)置的太小導(dǎo)致老表的容量沒有超過16導(dǎo)致的)口猜。
5负溪,將當(dāng)前閾值設(shè)置為剛計(jì)算出來的新的閾值透揣,定義新表,容量為剛計(jì)算出來的新容量川抡,將當(dāng)前的表設(shè)置為新定義的表辐真。
6,如果老表不為空崖堤,則需遍歷所有節(jié)點(diǎn)侍咱,將節(jié)點(diǎn)賦值給新表。
7密幔,將老表上索引為j的頭結(jié)點(diǎn)賦值給e節(jié)點(diǎn)楔脯,并將老表上索引為j的節(jié)點(diǎn)設(shè)置為空。
8胯甩,如果e的next節(jié)點(diǎn)為空昧廷,則代表老表的該位置只有1個(gè)節(jié)點(diǎn)堪嫂,通過hash值計(jì)算新表的索引位置,直接將該節(jié)點(diǎn)放在新表的該位置上木柬。
9皆串,如果e的next節(jié)點(diǎn)不為空,并且e為TreeNode眉枕,則調(diào)用split方法(見下文代碼塊10)進(jìn)行hash分布恶复。
10,如果e的next節(jié)點(diǎn)不為空速挑,并且e為普通的鏈表節(jié)點(diǎn)谤牡,則進(jìn)行普通的hash分布。
11姥宝,如果e的hash值與老表的容量(為一串只有1個(gè)為2的二進(jìn)制數(shù)拓哟,例如16為0000 0000 0001 0000)進(jìn)行位與運(yùn)算為0,則說明e節(jié)點(diǎn)擴(kuò)容后的索引位置跟老表的索引位置一樣(見例子1)伶授,進(jìn)行鏈表拼接操作:如果loTail為空断序,代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn),則將loHead賦值為該節(jié)點(diǎn)糜烹;否則將節(jié)點(diǎn)添加在loTail后面违诗,并將loTail賦值為新增的節(jié)點(diǎn)。
12疮蹦,如果e的hash值與老表的容量(為一串只有1個(gè)為2的二進(jìn)制數(shù)诸迟,例如16為0000 0000 0001 0000)進(jìn)行位與運(yùn)算為1,則說明e節(jié)點(diǎn)擴(kuò)容后的索引位置為:老表的索引位置+oldCap(見例子1)愕乎,進(jìn)行鏈表拼接操作:如果hiTail為空阵苇,代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn),則將hiHead賦值為該節(jié)點(diǎn)感论;否則將節(jié)點(diǎn)添加在hiTail后面绅项,并將hiTail賦值為新增的節(jié)點(diǎn)。
13比肄,老表節(jié)點(diǎn)重新hash分布在新表結(jié)束后快耿,如果loTail不為空(說明老表的數(shù)據(jù)有分布到新表上原索引位置的節(jié)點(diǎn)),則將最后一個(gè)節(jié)點(diǎn)的next設(shè)為空芳绩,并將新表上原索引位置的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)掀亥;如果hiTail不為空(說明老表的數(shù)據(jù)有分布到新表上原索引+oldCap位置的節(jié)點(diǎn)),則將最后一個(gè)節(jié)點(diǎn)的next設(shè)為空妥色,并將新表上索引位置為原索引+oldCap的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)搪花。
14,返回新表。

final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
    TreeNode<K,V> b = this; // 拿到調(diào)用此方法的節(jié)點(diǎn)
    TreeNode<K,V> loHead = null, loTail = null; // 存儲(chǔ)跟原索引位置相同的節(jié)點(diǎn)
    TreeNode<K,V> hiHead = null, hiTail = null; // 存儲(chǔ)索引位置為:原索引+oldCap的節(jié)點(diǎn)
    int lc = 0, hc = 0;
    for (TreeNode<K,V> e = b, next; e != null; e = next) {  // 從b節(jié)點(diǎn)開始遍歷
        next = (TreeNode<K,V>)e.next;   // next賦值為e的下個(gè)節(jié)點(diǎn)
        e.next = null;  // 同時(shí)將老表的節(jié)點(diǎn)設(shè)置為空撮竿,以便垃圾收集器回收
        //如果e的hash值與老表的容量進(jìn)行與運(yùn)算為0,則擴(kuò)容后的索引位置跟老表的索引位置一樣
        if ((e.hash & bit) == 0) {  
            if ((e.prev = loTail) == null)  // 如果loTail為空, 代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn)
                loHead = e; // 則將loHead賦值為第一個(gè)節(jié)點(diǎn)
            else
                loTail.next = e;    // 否則將節(jié)點(diǎn)添加在loTail后面
            loTail = e; // 并將loTail賦值為新增的節(jié)點(diǎn)
            ++lc;   // 統(tǒng)計(jì)原索引位置的節(jié)點(diǎn)個(gè)數(shù)
        }
        //如果e的hash值與老表的容量進(jìn)行與運(yùn)算為1,則擴(kuò)容后的索引位置為:老表的索引位置+oldCap
        else {  
            if ((e.prev = hiTail) == null)  // 如果hiHead為空, 代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn)
                hiHead = e; // 則將hiHead賦值為第一個(gè)節(jié)點(diǎn)
            else
                hiTail.next = e;    // 否則將節(jié)點(diǎn)添加在hiTail后面
            hiTail = e; // 并將hiTail賦值為新增的節(jié)點(diǎn)
            ++hc;   // 統(tǒng)計(jì)索引位置為原索引+oldCap的節(jié)點(diǎn)個(gè)數(shù)
        }
    }
 
    if (loHead != null) {   // 原索引位置的節(jié)點(diǎn)不為空
        if (lc <= UNTREEIFY_THRESHOLD)  // 節(jié)點(diǎn)個(gè)數(shù)少于6個(gè)則將紅黑樹轉(zhuǎn)為鏈表結(jié)構(gòu)
            tab[index] = loHead.untreeify(map);
        else {
            tab[index] = loHead;    // 將原索引位置的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)
            // hiHead不為空則代表原來的紅黑樹(老表的紅黑樹由于節(jié)點(diǎn)被分到兩個(gè)位置)
            // 已經(jīng)被改變, 需要重新構(gòu)建新的紅黑樹
            if (hiHead != null) 
                loHead.treeify(tab);    // 以loHead為根結(jié)點(diǎn), 構(gòu)建新的紅黑樹
        }
    }
    if (hiHead != null) {   // 索引位置為原索引+oldCap的節(jié)點(diǎn)不為空
        if (hc <= UNTREEIFY_THRESHOLD)  // 節(jié)點(diǎn)個(gè)數(shù)少于6個(gè)則將紅黑樹轉(zhuǎn)為鏈表結(jié)構(gòu)
            tab[index + bit] = hiHead.untreeify(map);
        else {
            tab[index + bit] = hiHead;  // 將索引位置為原索引+oldCap的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)
            // loHead不為空則代表原來的紅黑樹(老表的紅黑樹由于節(jié)點(diǎn)被分到兩個(gè)位置)
            // 已經(jīng)被改變, 需要重新構(gòu)建新的紅黑樹
            if (loHead != null) 
                hiHead.treeify(tab);    // 以hiHead為根結(jié)點(diǎn), 構(gòu)建新的紅黑樹
        }
    }
}

1丁稀,以調(diào)用此方法的節(jié)點(diǎn)開始,遍歷整個(gè)紅黑樹節(jié)點(diǎn)(此處實(shí)際是遍歷的鏈表節(jié)點(diǎn)倚聚,上文提過线衫,紅黑樹節(jié)點(diǎn)也會(huì)同時(shí)維護(hù)鏈表結(jié)構(gòu))。
2惑折,如果e的hash值與老表的容量(為一串只有1個(gè)為2的二進(jìn)制數(shù)授账,例如16為0000 0000 0001 0000)進(jìn)行位與運(yùn)算為0,則說明e節(jié)點(diǎn)擴(kuò)容后的索引位置跟老表的索引位置一樣(見下文例子1)惨驶,進(jìn)行鏈表拼接操作:如果loTail為空白热,代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn),則將loHead賦值為該節(jié)點(diǎn)粗卜;否則將節(jié)點(diǎn)添加在loTail后面屋确,并將loTail賦值為新增的節(jié)點(diǎn),并統(tǒng)計(jì)原索引位置的節(jié)點(diǎn)個(gè)數(shù)续扔。
3攻臀,如果e的hash值與老表的容量(為一串只有1個(gè)為2的二進(jìn)制數(shù),例如16為0000 0000 0001 0000)進(jìn)行位與運(yùn)算為1纱昧,則說明e節(jié)點(diǎn)擴(kuò)容后的索引位置為:老表的索引位置+oldCap(見例子1)刨啸,進(jìn)行鏈表拼接操作:如果hiTail為空,代表該節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn)识脆,則將hiHead賦值為該節(jié)點(diǎn)设联;否則將節(jié)點(diǎn)添加在hiTail后面,并將hiTail賦值為新增的節(jié)點(diǎn)灼捂,并統(tǒng)計(jì)索引位置為原索引+oldCap的節(jié)點(diǎn)個(gè)數(shù)离例。
4,如果原索引位置的節(jié)點(diǎn)不為空:如果當(dāng)該索引位置節(jié)點(diǎn)數(shù)<=6個(gè)悉稠,調(diào)用untreeify方法(見下文代碼塊11)將紅黑樹節(jié)點(diǎn)轉(zhuǎn)為鏈表節(jié)點(diǎn)宫蛆;否則將原索引位置的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)(即loHead結(jié)點(diǎn)),如果判斷hiHead不為空則代表原來的紅黑樹(老表的紅黑樹由于節(jié)點(diǎn)被分到兩個(gè)位置)已經(jīng)被改變偎球,需要重新構(gòu)建新的紅黑樹洒扎,以loHead為根結(jié)點(diǎn),調(diào)用treeify方法(見上文代碼塊7)構(gòu)建新的紅黑樹衰絮。
5,如果索引位置為原索引+oldCap的節(jié)點(diǎn)不為空:如果當(dāng)該索引位置節(jié)點(diǎn)數(shù)<=6個(gè)磷醋,調(diào)用untreeify方法(見下文代碼塊11)將紅黑樹節(jié)點(diǎn)轉(zhuǎn)為鏈表節(jié)點(diǎn)猫牡;否則將索引位置為原索引+oldCap的節(jié)點(diǎn)設(shè)置為對(duì)應(yīng)的頭結(jié)點(diǎn)(即hiHead結(jié)點(diǎn)),如果判斷l(xiāng)oHead不為空則代表原來的紅黑樹(老表的紅黑樹由于節(jié)點(diǎn)被分到兩個(gè)位置)已經(jīng)被改變邓线,需要重新構(gòu)建新的紅黑樹淌友,以hiHead為根結(jié)點(diǎn)煌恢,調(diào)用treeify方法(見上文代碼塊7)構(gòu)建新的紅黑樹。

// 將紅黑樹節(jié)點(diǎn)轉(zhuǎn)為鏈表節(jié)點(diǎn), 當(dāng)節(jié)點(diǎn)<=6個(gè)時(shí)會(huì)被觸發(fā)
final Node<K,V> untreeify(HashMap<K,V> map) {  
    Node<K,V> hd = null, tl = null; // hd指向頭結(jié)點(diǎn), tl指向尾節(jié)點(diǎn)
    // 從調(diào)用該方法的節(jié)點(diǎn), 即鏈表的頭結(jié)點(diǎn)開始遍歷, 將所有節(jié)點(diǎn)全轉(zhuǎn)為鏈表節(jié)點(diǎn)
    for (Node<K,V> q = this; q != null; q = q.next) {   
        // 調(diào)用replacementNode方法構(gòu)建鏈表節(jié)點(diǎn)
        Node<K,V> p = map.replacementNode(q, null); 
        // 如果tl為null, 則代表當(dāng)前節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn), 將hd賦值為該節(jié)點(diǎn)
        if (tl == null)
            hd = p;
        else    // 否則, 將尾節(jié)點(diǎn)的next屬性設(shè)置為當(dāng)前節(jié)點(diǎn)p
            tl.next = p;
        tl = p; // 每次都將tl節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn), 即尾節(jié)點(diǎn)
    }
    return hd;  // 返回轉(zhuǎn)換后的鏈表的頭結(jié)點(diǎn)
}

1震庭,從調(diào)用該方法的節(jié)點(diǎn)瑰抵,即鏈表的頭結(jié)點(diǎn)開始遍歷, 將所有節(jié)點(diǎn)全轉(zhuǎn)為鏈表節(jié)點(diǎn)
2,調(diào)用replacementNode方法構(gòu)建鏈表節(jié)點(diǎn)
3器联,如果tl為null, 則代表當(dāng)前節(jié)點(diǎn)為第一個(gè)節(jié)點(diǎn)二汛,將hd賦值為該節(jié)點(diǎn),否則, 將尾節(jié)點(diǎn)的next屬性設(shè)置為當(dāng)前節(jié)點(diǎn)p
4拨拓,每次都將tl節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn), 即尾節(jié)點(diǎn)
5肴颊,返回轉(zhuǎn)換后的鏈表的頭結(jié)點(diǎn)

總結(jié)

1,HashMap的底層是個(gè)Node數(shù)組(Node<K,V>[] table)渣磷,在數(shù)組的具體索引位置婿着,如果存在多個(gè)節(jié)點(diǎn),則可能是以鏈表或紅黑樹的形式存在醋界。
2竟宋,增加、刪除形纺、查找鍵值對(duì)時(shí)袜硫,定位到哈希桶數(shù)組的位置是很關(guān)鍵的一步,源碼中是通過下面3個(gè)操作來完成這一步:1)拿到key的hashCode值挡篓;2)將hashCode的高位參與運(yùn)算婉陷,重新計(jì)算hash值;3)將計(jì)算出來的hash值與(table.length - 1)進(jìn)行&運(yùn)算官研。
3秽澳,HashMap的默認(rèn)初始容量(capacity)是16,capacity必須為2的冪次方戏羽;默認(rèn)負(fù)載因子(load factor)是0.75担神;實(shí)際能存放的節(jié)點(diǎn)個(gè)數(shù)(threshold,即觸發(fā)擴(kuò)容的閾值)= capacity * load factor始花。
4妄讯,HashMap在觸發(fā)擴(kuò)容后,閾值會(huì)變?yōu)樵瓉淼?倍酷宵,并且會(huì)進(jìn)行重hash亥贸,重hash后索引位置index的節(jié)點(diǎn)的新分布位置最多只有兩個(gè):原索引位置或原索引+oldCap位置。例如capacity為16浇垦,索引位置5的節(jié)點(diǎn)擴(kuò)容后炕置,只可能分布在新報(bào)索引位置5和索引位置21(5+16)。
5,導(dǎo)致HashMap擴(kuò)容后朴摊,同一個(gè)索引位置的節(jié)點(diǎn)重hash最多分布在兩個(gè)位置的根本原因是:1)table的長度始終為2的n次方默垄;2)索引位置的計(jì)算方法為“(table.length - 1) & hash”。HashMap擴(kuò)容是一個(gè)比較耗時(shí)的操作甚纲,定義HashMap時(shí)盡量給個(gè)接近的初始容量值口锭。
6,HashMap有threshold屬性和loadFactor屬性介杆,但是沒有capacity屬性鹃操。初始化時(shí),如果傳了初始化容量值这溅,該值是存在threshold變量组民,并且Node數(shù)組是在第一次put時(shí)才會(huì)進(jìn)行初始化,初始化時(shí)會(huì)將此時(shí)的threshold值作為新表的capacity值悲靴,然后用capacity和loadFactor計(jì)算新表的真正threshold值臭胜。
7,當(dāng)同一個(gè)索引位置的節(jié)點(diǎn)在增加后達(dá)到9個(gè)時(shí)癞尚,并且此時(shí)數(shù)組的長度大于等于64耸三,則會(huì)觸發(fā)鏈表節(jié)點(diǎn)(Node)轉(zhuǎn)紅黑樹節(jié)點(diǎn)(TreeNode,間接繼承Node)浇揩,轉(zhuǎn)成紅黑樹節(jié)點(diǎn)后仪壮,其實(shí)鏈表的結(jié)構(gòu)還存在,通過next屬性維持胳徽。鏈表節(jié)點(diǎn)轉(zhuǎn)紅黑樹節(jié)點(diǎn)的具體方法為源碼中的treeifyBin(Node<K,V>[] tab, int hash)方法积锅。而如果數(shù)組長度小于64,則不會(huì)觸發(fā)鏈表轉(zhuǎn)紅黑樹养盗,而是會(huì)進(jìn)行擴(kuò)容缚陷。
8,當(dāng)同一個(gè)索引位置的節(jié)點(diǎn)在移除后達(dá)到6個(gè)時(shí)往核,并且該索引位置的節(jié)點(diǎn)為紅黑樹節(jié)點(diǎn)箫爷,會(huì)觸發(fā)紅黑樹節(jié)點(diǎn)轉(zhuǎn)鏈表節(jié)點(diǎn)。紅黑樹節(jié)點(diǎn)轉(zhuǎn)鏈表節(jié)點(diǎn)的具體方法為源碼中的untreeify(HashMap<K,V> map)方法聂儒。
9虎锚,HashMap在JDK1.8之后不再有死循環(huán)的問題,JDK1.8之前存在死循環(huán)的根本原因是在擴(kuò)容后同一索引位置的節(jié)點(diǎn)順序會(huì)反掉衩婚。
10窜护,HashMap是非線程安全的,在并發(fā)場景下使用ConcurrentHashMap來代替谅猾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末柄慰,一起剝皮案震驚了整個(gè)濱河市鳍悠,隨后出現(xiàn)的幾起案子税娜,更是在濱河造成了極大的恐慌坐搔,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敬矩,死亡現(xiàn)場離奇詭異概行,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)弧岳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門凳忙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人禽炬,你說我怎么就攤上這事涧卵。” “怎么了腹尖?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵柳恐,是天一觀的道長。 經(jīng)常有香客問我热幔,道長乐设,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任绎巨,我火速辦了婚禮近尚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘场勤。我一直安慰自己戈锻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布和媳。 她就那樣靜靜地躺著格遭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窗价。 梳的紋絲不亂的頭發(fā)上如庭,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音撼港,去河邊找鬼坪它。 笑死,一個(gè)胖子當(dāng)著我的面吹牛帝牡,可吹牛的內(nèi)容都是我干的往毡。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼今瀑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岂贩,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤嗤详,失蹤者是張志新(化名)和其女友劉穎个扰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葱色,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡递宅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苍狰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片办龄。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖淋昭,靈堂內(nèi)的尸體忽然破棺而出俐填,到底是詐尸還是另有隱情,我是刑警寧澤翔忽,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布英融,位于F島的核電站,受9級(jí)特大地震影響呀打,放射性物質(zhì)發(fā)生泄漏矢赁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一贬丛、第九天 我趴在偏房一處隱蔽的房頂上張望撩银。 院中可真熱鬧,春花似錦豺憔、人聲如沸额获。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抄邀。三九已至,卻和暖如春昼榛,著一層夾襖步出監(jiān)牢的瞬間境肾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工胆屿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奥喻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓非迹,卻偏偏與公主長得像环鲤,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子憎兽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容