HashMap(JDK1.8) resize()源码分析

1,790 阅读3分钟

1. 源码

final Node<K,V>[] resize() {
        /**
        * 1. oldCap > 0 首先确定扩容后的table长度newCap
        *   - 如果超过MAXIMUM_CAPACITY(1<<30)则不再扩容
        *   - 否则扩容2倍(2 * oldCap < MAXIMUM_CAPACITY && oldCap > 16) 
              newThr = oldThr << 1;
        * 2. oldThr > 0 (initial capacity was placed in threshold)
        *   - 这里oldCap == 0,表示表是空的,但是有oldThr,所有newCap = oldThr;
        * 3. oldCap == 0 && oldThr == 0 (zero initial threshold signifies using defaults)
             这里表示表是空的并且oldThr = 0,则
        *   -newCap = DEFAULT_INITIAL_CAPACITY(16)
        *   -newThr = 16 * 0.75 = 12
        * 4. 如果newThr == 0,求个新的threshold,并且赋给threshold
        */
        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;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        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) {
                    //旧的设置为null,用于GC
                    oldTab[j] = null;
                    //如果只有一个node
                    if (e.next == null)
                        //只有一个元素,获取新的下标,注意使用的是newCap 
                        //这里注意到新的jdk1.8中hash函数是:高16bit不变,低16bit和高16bit做了一个异或,
                          然后再(n - 1) & hash获取下标
                        newTab[e.hash & (newCap - 1)] = e;
                    //如果是红黑树
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        
                        /**
                        *  核心主要是这里的扩容机制
                        *  1. 先说一些概念,扩容后容量翻倍,所以会有低位和高位之分,
                             低位就是原来的长度,比如(0-15),高位(16-31)
                        */
                        
                        // 1. 定义高低位头尾节点
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        // 2. 临时节点
                        Node<K,V> next;
                        do {
                            next = e.next;
                            // 3. 利用位运算判断高低位, == 0表示< oldCap,属于低位,可以看下面的分析
                            if ((e.hash & oldCap) == 0) {
                                // 4. 利用尾插法插入值
                                // loTail == null,
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            // 5. 高位同样处理
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        //低位的在原来的index
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        //高位的在新index
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

2. 扩容后如何确定index(e.hash & oldCap)

初始大小1 << 4 = 2^4 = 16
16 扩容成 32,1 << 4 << 1 = 32 高位是1
下面hash1和hash2对oldCap异或操作,
1. hash1高位是0,0 & 1 = 0,在旧的index j
2. hash2高位是1,1 & 1 = 1,高位是1,即相当于j + oldCap
所以扩容不需要重新计算hash值,只需要判断原来的hash值和oldCap位运算,利用高位确定

16扩容成32:

16扩容成32

e.hash & oldCap 举个例子:

Map map = new HashMap<>(8,0.75f);
map.put("1","1");
map.put("2","2");
map.put("3","3");
map.put("10","10");
map.put("18","18");
map.put("6","6");
map.put("7","7");
map.put("8","8");
System.out.println(map);
初始大小8,负载因子0.75f,长度到达7就会扩容,debug会发现
oldCap = 8
10的hash值是1567
0110 0001 1111
0000 0000 1000 与运算 = 8 高位
18的hash值是1575
0110 0010 0111
0000 0000 1000 与运算 = 0 低位

参考:
-HashMap源码注解 之 resize()方法(七)