JDK8 HashMap put() 方法源码分析

一、前置知识

红黑树定义

红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  1. 结点是红色或黑色。
  2. 根结点是黑色。
  3. 所有叶子都是黑色。(叶子是NIL结点)
  4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
  5. 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。

二、构造方法

HashMap()

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

HashMap(int initialCapacity, float loadFactor)

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    // 如果指定的容量大于 1 << 30, 则指定为最大值
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    // 将非2的幂次的容量,指定为2的幂次
    // hashmap的容量是2的幂,如果传入的参数不是2的幂,则会算出比该参数大的最小的2的幂次的数;例如,传入3,则容量4;传入12,则容量16
    this.threshold = tableSizeFor(initialCapacity);
}

tableSizeFor(int cap):计算hashmap初始容量

static final int tableSizeFor(int cap) {
	// 减1之后,用最高位的 1 把后面每一位都变成 1,此时再加1时,就是2的幂次
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

三、put 方法源码

1. put()

public V put(K key, V value) {
	// 计算 key 的 hash 值
    return putVal(hash(key), key, value, false, true);
}

hash(Object key):计算key的hash值

/**
 * 计算 key.hashCode() 并将散列的高位散布 (XOR) 到低位。
 * 因为该表使用二次方掩码,所以仅在当前掩码以上的位上有所不同的散列集将始终发生冲突。 (在已知的例子中有一组 Float 键在小表中保存连续的整数。)
 * 所以我们应用一个转换来向下传播较高位的影响。在位扩展的速度、效用和质量之间存在权衡。
 * 因为许多常见的哈希集已经合理分布(因此不会从传播中受益),并且因为我们使用树来处理 bins 中的大量冲突,所以我们只是以最便宜的方式对一些移位的位进行 XOR 以减少系统损失,以及合并最高位的影响,否则由于表边界而永远不会在索引计算中使用这些位。
 */
static final int hash(Object key) {
    int h;
    // key hashcode 的值(32位),高16位与低16位按位异或
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2. putVal()

该方法主要考虑以下几种情况:

放值前:

  1. 初始化时,未指定 HashMap 的容量,设置默认容量为 16

存放值时:

  1. 数组节点上没有存任何 key,即没有发生 hash 碰撞
  2. 数组节点的 key 和新 put 的 key 一样
  3. 数组节点是红黑树
  4. 数组节点是链表

放完后:

  1. 检查 hashmap 中所有元素个数,超过 负载因子*容量,则扩容
/**
 * Implements Map.put and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyIfAbsent if true, don't change existing value
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    // 访问局部变量而不是类属性的理解:访问栈空间比访问堆空间更快(逃逸分析、栈上分配)
    // tab:数组;p:point,指针,当前访问的节点;n:数组大小;i:k hash值对应的数组下标
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
    	// 初始化时,未指定 HashMap 的容量,容量为 0,需先扩容
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
    	// 数组上的节点中没有存值
        tab[i] = newNode(hash, key, value, null);
    else {
    	// e:hashmap 中存在key和新插入key相同的旧节点
    	// k:旧节点的 key
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            // 数组节点上的 key 和新插入的 key 一致
            e = p;
        else if (p instanceof TreeNode)
        	// 如果是红黑树
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
        	// 如果是链表
            for (int binCount = 0; ; ++binCount) {
            	// 遍历链表
                if ((e = p.next) == null) {
                	// 链表尾结点
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    	// 当第9个插入完成后(此时binCount=7),链表转成红黑树
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    // 遍历链表时发现的相同key的情况
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
            	// 存在旧的值,如果是 put() 方法,就替换,如果是 putIfAbsent() 方法就不替换
                e.value = value;
            afterNodeAccess(e);
            // 返回旧值
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
    	// 如果数组中元素个数大于 容量*负载因子,则扩容
        resize();
    afterNodeInsertion(evict);
    return null;
}

通过 hash 计算数组下标

通过 hash 值计算对应的数组下标:(n - 1) & hash

n 是数组大小,是 2 的幂,对应的二进制就是最高位是 1,其余位全是 0
在这里插入图片描述

我对该算法的理解:

  1. n-1 后,每个位上的值必须都是 1,所以 n 必须是 2 的幂
  2. 因为该算法只是取 int 的低位,所以在计算 hash 值的时候,将高16位与低16位进行异或运算,减少高位不同而低位相同产生的 hash 碰撞

3. resize():扩容

/**
 * 初始化或表大小 * 2。
 * 如果为空,则在最大值中保持的初始容量目标。
 * 否则,数组大小*2,hashmap 中每个元素必须保持在相同的索引中或者在新表的2的幂的偏移中
 */
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) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            // 数组已经是最大容量的情况,不扩容,扩容门槛赋值为最大值
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // 容量*2
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                oldCap >= DEFAULT_INITIAL_CAPACITY)
            // 扩容后容量<最大容量且旧容量>=16时,扩展门槛*2
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        // 设置默认容量大小是 16,扩容门槛(容量*负载因子)是12
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        // 扩容门槛为 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
                    // 数组中是链表的情况
                    // 高位和低位的理解:扩容后,原先相同hash的key,只会被分到新数组的两个位置,大的位置叫高位,小的位置叫低位
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            // 低位
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        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;
}

扩容时计算数组下标

在这里插入图片描述

4. treeifyBin(tab, hash):链表转为红黑树

/**
 * 替换给定哈希的索引处所有链表节点,除非表太小,在这种情况下会扩容。
 */
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)
        // 如果数组没有被初始化或者数组长度小于64,扩容
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        // hd(head):头结点 tl(tail):尾结点
        TreeNode<K,V> hd = null, tl = null;
        do {
            // 将 Node 结点转换为 TreeNode 结点
            TreeNode<K,V> p = replacementTreeNode(e, null);
            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);
    }
}

treeify():双向链表转为红黑树

final void treeify(Node<K,V>[] tab) {
    TreeNode<K,V> root = null;
    for (TreeNode<K,V> x = this, next; x != null; x = next) {  // x/this:双向链表头节点
        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;
            for (TreeNode<K,V> p = root;;) {
                int dir, ph;  //dir:标识大小(负数插入左子树中,正数插入右子树中);ph:父节点hash值;pk:父节点key
                K pk = p.key;
                if ((ph = p.hash) > h)  //要插入的节点hash值比父节点小(往左继续迭代)
                    dir = -1;
                else if (ph < h)  //要插入的节点hash值比父节点大(往右继续迭代)
                    dir = 1;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||  // hash相同但key不相同,且没有实现Comparable接口
                         (dir = compareComparables(kc, k, pk)) == 0)  // 或者key实现了Comparable接口,调用compareTo()方法返回0
                    dir = tieBreakOrder(k, pk);

                TreeNode<K,V> xp = p;
                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);  //root节点放入数组中,且root节点是双向链表头节点
}
comparableClassFor():是否实现了 Comparable 接口

是否实现了 Comparable 接口,如果实现了则返回对应的 Class 对象,没有实现则返回 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) // String类型直接返回
            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) &&  // 如果实现了Comparable接口就继续往下走
                    (as = p.getActualTypeArguments()) != null &&  //获取Comparable<>中的泛型数组
                    as.length == 1 && as[0] == c) // 泛型长度是1且类型是c,则返回
                    return c;
            }
        }
    }
    return null;  // 没有实现Comparable则返回Null
}

ParameterizedType 和使用:

compareComparables():调用Comparable接口的compareTo()方法
static int compareComparables(Class<?> kc, Object k, Object x) {
    return (x == null || x.getClass() != kc ? 0 :
            ((Comparable)k).compareTo(x));
}
tieBreakOrder():当key的比较结果相同时,打破平衡(使比较结果不相等)
static int tieBreakOrder(Object a, Object b) {
    int d;
    if (a == null || b == null ||
        (d = a.getClass().getName().
         compareTo(b.getClass().getName())) == 0)  // 先类名比较
        d = (System.identityHashCode(a) <= System.identityHashCode(b) ?  // 类名也相同的情况比较地址hash
             -1 : 1);
    return d;
}

5. split():分割红黑树

/**
 * 将红黑树中的节点拆分为低位和高位的双向链表,如果红黑树里结点数太少,则退化为单向链表。
 */
final void split(java.util.HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
    // 位于数组上的结点
    TreeNode<K,V> b = this;
    // Relink into lo and hi lists, preserving order
    // 低位双向链表头和尾
    TreeNode<K,V> loHead = null, loTail = null;
    // 高位双向链表头和尾
    TreeNode<K,V> hiHead = null, hiTail = null;
    // lc(low count):低位结点数量 hc(high count):高位结点数量
    int lc = 0, hc = 0;
    for (TreeNode<K,V> e = b, next; e != null; e = next) {
        next = (TreeNode<K,V>)e.next;
        e.next = null;
        if ((e.hash & bit) == 0) {
            // 低位
            if ((e.prev = loTail) == null)
                // 链表为空,则该结点就是头结点
                loHead = e;
            else
                // 结点拼接到链表尾部
                loTail.next = e;
            loTail = e;
            ++lc;
        }
        else {
            // 高位
            if ((e.prev = hiTail) == null)
                hiHead = e;
            else
                hiTail.next = e;
            hiTail = e;
            ++hc;
        }
    }

    if (loHead != null) {
        if (lc <= UNTREEIFY_THRESHOLD)
            // 如果低位链表结点数量少于6,则红黑树退化为单向链表
            tab[index] = loHead.untreeify(map);
        else {
            tab[index] = loHead;
            if (hiHead != null) // (else is already treeified)
                // 双向链表转换为红黑树
                loHead.treeify(tab);
        }
    }
    if (hiHead != null) {
        // 高位的处理和低位相同
        if (hc <= UNTREEIFY_THRESHOLD)
            tab[index + bit] = hiHead.untreeify(map);
        else {
            tab[index + bit] = hiHead;
            if (loHead != null)
                hiHead.treeify(tab);
        }
    }
}

untreeify():双向链表转单向链表

final Node<K,V> untreeify(java.util.HashMap<K,V> map) {
    Node<K,V> hd = null, tl = null;
    // q 是数组中的红黑树结点
    for (Node<K,V> q = this; q != null; q = q.next) {
    	// 将 TreeNode 对象替换为 Node 对象(红黑树结点转换为单向链表结点)
        Node<K,V> p = map.replacementNode(q, null);
        if (tl == null)
            hd = p;
        else
            tl.next = p;
        tl = p;
    }
    return hd;
}

6. putTreeVal():往红黑树中插入节点

与 treeify() 方法相比:

  • treeify():

    • 对红黑树根节点的判断
  • putTreeVal():

    • 多了 key 值相同时的判断

红黑树插入后平衡调整规则:

规定新插入的节点是红色:

  1. 根节点直接插入,变为黑色
  2. 如果父节点是黑色,则直接插入
  3. 如果父节点是红色:
    1. 叔叔节点是空或黑色,旋转加变色
    2. 叔叔节点是红色,父节点和叔叔节点变黑色,祖父节点变红色

在这里插入图片描述

final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                               int h, K k, V v) { //this:TreeNode;h:要插入的key的hash值;k:要插入的key;v:要插入的value
    Class<?> kc = null;
    boolean searched = false;
    TreeNode<K,V> root = (parent != null) ? root() : this; //找到root节点
    for (TreeNode<K,V> p = root;;) {
        int dir, ph; K pk;  //dir:正负标识大小;ph:父节点hash值;pk:父节点key
        if ((ph = p.hash) > h)  //要插入的节点hash值比父节点小(往左继续迭代)
            dir = -1;
        else if (ph < h)  //要插入的节点hash值比父节点大(往右继续迭代)
            dir = 1;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))  //key值相同
            return p;
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||  // hash相同但key不相同,且没有实现Comparable接口
                 (dir = compareComparables(kc, k, pk)) == 0) {  // 或者key实现了Comparable接口,调用compareTo()方法返回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;  // 查到了相同的key,就返回
            }
            dir = tieBreakOrder(k, pk);  //打破要插入节点和父节点相等的状态(类名排序,类名相同时identityHashCode排序)
        }

        TreeNode<K,V> xp = p;
        if ((p = (dir <= 0) ? p.left : p.right) == null) { //<=0时走左边,>0时走右边
        	// 当遍历到叶子节点时
            Node<K,V> xpn = xp.next;
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);  //创建节点,next节点指向xpn(双向链表角度看,新节点插到了父节点后面)
            if (dir <= 0)
                xp.left = x;  //叶子节点赋值
            else
                xp.right = x;
            xp.next = x;  //父节点next指针指向新节点
            x.parent = x.prev = xp;  //新节点的parent和prev指针指向父节点
            if (xpn != null)
                ((TreeNode<K,V>)xpn).prev = x;  // 双向链表指针调整
            moveRootToFront(tab, balanceInsertion(root, x));  // 平衡红黑树后,并root节点放入数组中,且root节点是双向链表头节点
            return null;
        }
    }
}

find():在红黑树中查找

final TreeNode<K,V> find(int h, Object k, Class<?> kc) {  // h:被查找key的hash;k:被查找的key;kc:被查找的key的Class对象
    TreeNode<K,V> p = this;  // this是要查找树的根节点
    do {
        int ph, dir; K pk;
        TreeNode<K,V> pl = p.left, pr = p.right, q;
        if ((ph = p.hash) > h)  //往左遍历
            p = pl;
        else if (ph < h)  //往右遍历
            p = pr;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))  // 找到了对应的Key
            return p;  //将找到的Key返回
        // 下面都是hash冲突的情况
        else if (pl == null)
            p = pr;
        else if (pr == null)
            p = pl;
        else if ((kc != null ||  
                  (kc = comparableClassFor(k)) != null) &&
                 (dir = compareComparables(kc, k, pk)) != 0)  //key实现了Comparable接口的情况
            p = (dir < 0) ? pl : pr;
        else if ((q = pr.find(h, k, kc)) != null)  //比较不出大小来,先右子树继续遍历
            return q;
        else  //最后左子树遍历
            p = pl;
    } while (p != null);
    return null;  // 没有查到返回null
}

balanceInsertion():(红黑树中插入值后)平衡红黑树

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,  // 红黑树根节点
                                            TreeNode<K,V> x) {  // 新插入的节点
    x.red = true;  //新插入的节点是红色的
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) { //xp:父节点;xpp:祖父节点;xppl:祖父节点左孩子结点;xppr:祖父节点右孩子节点
        if ((xp = x.parent) == null) { //新插入的节点是根节点的情况
            x.red = false;
            return x;
        }
        else if (!xp.red || (xpp = xp.parent) == null) //父节点是黑的,或者父节点是根节点
            return root;
        //以下都是父节点是红色的情况
        if (xp == (xppl = xpp.left)) {  //xp 在 xpp 左子树的情况(如下图)
            if ((xppr = xpp.right) != null && xppr.red) {  // 如图1,叔叔(右)节点是红色的
                xppr.red = false;  //叔叔节点和父节点变黑
                xp.red = false;
                xpp.red = true;   //祖父节点变红,如图2
                x = xpp;  //继续迭代
            }
            else {   // 图3
                if (x == xp.right) {
                    root = rotateLeft(root, x = xp);  //左旋 图4
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;  // 图5
                        root = rotateRight(root, xpp);  //右旋,如图6
                    }
                }
            }
        }
        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);  // 再左旋
                    }
                }
            }
        }
    }
}

在这里插入图片描述

rotateLeft():左旋

static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                      TreeNode<K,V> p) {
    TreeNode<K,V> r, pp, rl;
    if (p != null && (r = p.right) != null) { 
        if ((rl = p.right = r.left) != null)  // 图2
            rl.parent = p;
        if ((pp = r.parent = p.parent) == null)
            (root = r).red = false;
        else if (pp.left == p)
            pp.left = r;
        else
            pp.right = r;  // 图3
        r.left = p;	 // 图4
        p.parent = r;  
    }
    return root;
}

在这里插入图片描述

rotateRight():右旋

与左旋操作相反

static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                       TreeNode<K,V> p) {
    TreeNode<K,V> l, pp, lr;
    if (p != null && (l = p.left) != null) {
        if ((lr = p.left = l.right) != null)
            lr.parent = p;
        if ((pp = l.parent = p.parent) == null)
            (root = l).red = false;
        else if (pp.right == p)
            pp.right = l;
        else
            pp.left = l;
        l.right = p;
        p.parent = l;
    }
    return root;
}

moveRootToFront():将 root 节点放入数组中

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;  // 数组中放root节点
            // 将root节点从双向链表中删除
            TreeNode<K,V> rp = root.prev;
            if ((rn = root.next) != null)  
                ((TreeNode<K,V>)rn).prev = rp;
            if (rp != null)
                rp.next = rn;
            // root节点放到first节点前面(双向链表头节点)
            if (first != null)
                first.prev = root;
            root.next = first;
            root.prev = null;
        }
        assert checkInvariants(root);
    }
}

总结

  1. 容量是2的幂次,当存的数达到 容量*0.75 时,扩容
  2. 数组先存单向链表,链表上的节点个数超过8时,如果数组大小没有达到64,则扩容,否则链表转换成双向链表(仍然存在)再转换成红黑树
  3. 扩容时,如果红黑树中的节点个数小于等于6,则红黑树退化成单向链表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值