HashMap之resize

本文深入解析了HashMap的resize方法,详细介绍了如何初始化或加倍表的大小,以及在扩容过程中元素如何重新定位。首先,根据map的当前状态计算新的容量和阈值;然后,通过循环遍历旧表,将元素重新分配到新表中,确保链表的顺序不变。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
 * Initializes or doubles table size.  If null, allocates in
 * accord with initial capacity target held in field threshold.
 * Otherwise, because we are using power-of-two expansion, the
 * elements from each bin must either stay at same index, or move
 * with a power of two offset in the new table.
 *
 * @return the table
 */
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;
        }
        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) {
                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;
                        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;
}

注释

初始化或加倍表的大小。若为空,按字段阈值中持有的初始容量目标分配。否则,由于我们使用的是2的幂展开,每个bin中的元素要么必须保持在相同的索引中,要么在新表中移动2的幂偏移量

第一部分

简述

主要计算map的需要扩容的大小

业务逻辑整理

  • 1,如果map有值
    • 1.1,map长度大于等于1 << 30 (即1073741824),则设置threshold为最大值
    • 1.2,map长度扩大一倍,任然小于1 << 30 (即1073741824),且大于初始值,则设置oldThr (threshold)扩大一倍
  • 2,如果map没有值,但是oldThr(threshold)大于0,则不改变oldThr(threshold)大小
  • 3,否则(map为空,oldThr小于0),newCap,newThr进行初始化
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
// 1,map有值,即oldCap(oldTab(table).length)大于0
if (oldCap > 0) {
	//1.1,扩容的长度已经达到最大值,即设置threshold为最大值
    if (oldCap >= MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return oldTab;
    }
    //1.2,扩容的长度没有达到最大值,即设置newThr扩容一倍
    else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
             oldCap >= DEFAULT_INITIAL_CAPACITY)
        newThr = oldThr << 1; // double threshold
}
//2如果map没有值,但是oldThr(threshold)大于0,则不改变oldThr(threshold)大小
else if (oldThr > 0) // initial capacity was placed in threshold
    newCap = oldThr;
//3,否则(map为空,oldThr小于0),newCap,newThr进行初始化
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;

第二部分

简述

主要重置对象的位置

业务逻辑整理

  • 1,如果table 不为空,循环table中对象
  • 2,如果e = oldTab[j]对象为空,进入下一个对象
  • 3,如果e = oldTab[j]不为空
    • 3.1,e对象链表中的下一个对象是为空,则重新计算e对象的下标(e.hash & (newCap - 1)),放入newTab对象中
    • 3.2,如果e对象链表中的下一个对象不为空,且是一个树结构,调用split函数,重置对象
    • 3.3,否则(即e对象链表中有值,又不是树结构),则循环链表,重置链表中的对象位置
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
	//1,如果table 不为空,循环table中对象
    for (int j = 0; j < oldCap; ++j) {
        Node<K,V> e;
        //3,如果e = oldTab[j]不为空
        if ((e = oldTab[j]) != null) {
            oldTab[j] = null;
            //3.1,e对象链表中的下一个对象是为空,则重新计算e对象的下标(e.hash & (newCap - 1)),放入newTab对象中
            if (e.next == null)
                newTab[e.hash & (newCap - 1)] = e;
            //3.2,如果e对象链表中的下一个对象不为空,且是一个树结构,调用split函数,重置对象
            else if (e instanceof TreeNode)
                ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
            //3.3,否则(即e对象链表中有值,又不是树结构),则循环链表,重置链表中的对象位置
            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;
                    //不需要移动的元素,拼接好链表
                    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;
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值