final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
//先判断之前的容器容量是否大于0    
 /*
        1、resize()函数在size > threshold时被调用。
            oldCap大于 0 代表原来的 table 表非空, oldCap 为原表的大小,
            oldThr(threshold) 为 oldCap × load_factor
 */
    if (oldCap > 0) {//如果大于0
//如果之前的容量超出容器容量最大值,那么将临界值赋予最大值,直接返回旧容器对象
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
//进行扩容,为之前的2倍,临界值也是一样 
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
//容器大小小于0,则指定临界值为容器大小,这个临界值是之前初始化的
/*
        2、resize()函数在table为空被调用。
        oldCap 小于等于 0 且 oldThr 大于0,代表用户创建了一个 HashMap,但是使用的构造函数为
        HashMap(int initialCapacity, float loadFactor) 或 HashMap(int initialCapacity)
        或 HashMap(Map<? extends K, ? extends V> m),导致 oldTab 为 null,oldCap 为0,
        oldThr 为用户指定的 HashMap的初始容量。
 */

    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
/*
            3、resize()函数在table为空被调用。
            oldCap 小于等于 0 且 oldThr 等于0,用户调用 HashMap()构造函数创建的 HashMap,所有值均采用默认值,
          oldTab(Table)表为空,oldCap为0,oldThr等于0,
*/
       
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }

//如果新制定的临界值为0重新指定    
    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
//如果扩容后,元素的index依然与原来一样,那么使用这个head和tail指针              
                    Node<K,V> loHead = null, loTail = null;
//如果扩容后,元素的index=index+oldCap,那么使用这个head和tail指针                 
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        //这个地方很巧妙,直接通过e.hash & oldCap得出e在newTab中的位置;
                        //因为table是2倍扩容,所以只需要看hash值与oldCap进行与操作,结果为0,那么还是原来的index;否则index = index + oldCap
                        
                        /*
                        通过与元素的hash值进行与操作,能够快速定位到数组下标
相对于取模运算,直接进行与操作能提高计算效率。在CPU中,所有的加减乘除都是通过加法实现的,而与操作时CPU直接支持的。
扩容时简化计算数组下标的计算量
因为数组每次扩容都是原来的两倍,所以每一个元素在新数组中的位置要么是原来的index,要么index = index + oldCap
                        */
                        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;
			 //还是原来的index                       
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
			//index 变为 index + oldCap                       
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

扩容是一个分厂消耗性能的工作,有些时候我们一直容量的情况下,尽量进行初始化,以避免扩容
而且在JDK1.8之前,扩容操作在多线程情况下很容易造成环形链表,如果get的话产生死循环的情况
在1.8中resize()方法不再调用transfer()方法,而是直接将原来transfer()方法中的代码写在自己方法体内;
当然表面上我们能看到方法的减少,其实还有一个重大改变,那就是:扩容后,新数组中的链表顺序依然与旧数组中的链表顺序保持一致!不再是之前的改变顺序了
之前是 a ->b->c 扩容后 c->b->a