Connor学Java - HashMap扩容机制

本文深入剖析了Java HashMap的扩容机制,详细解释了何时以及如何进行扩容。当HashMap达到其阈值时,会进行resize操作,容量翻倍。文章还涵盖了resize过程中的节点迁移,包括单链表和红黑树的处理。通过对源码的解析,读者可以全面理解HashMap的内部工作原理。
摘要由CSDN通过智能技术生成

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学Java系列,这个系列记录了我的Java基础知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/4Kxtm,话不多说我们马上开始!

HashMap扩容机制源码分析

有两种情况会调用resize方法

(1)第一次调用put方法时,会调用resize方法对table数组进行初始化。默认大小16

(2)扩容时调用resize,即size > threshold时,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;
    // 1
    if (oldCap > 0) {
        // 1-(1)
        if (oldCap >= MAXIMUM_CAPACITY) {
             threshold = Integer.MAX_VALUE;
             return oldTab;
        }
        // 1-(2)
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
             newThr = oldThr << 1; // double threshold
    }
    // 2
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    // 3
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    // 2-(1)
    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"})
    // 1
    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;
                // 2
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                // 3
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                // 4
                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;
}

先明确源码中的几个变量,方便之后分析

  • Node<K,V>[] oldTab = table:旧数组

  • int oldCap = (oldTab == null) ? 0 : oldTab.length:旧数组的容量

  • int oldThr = threshold:旧数组的阈值

  • int newCap, newThr = 0:新数组的容量和阈值

数组容量与阈值的变化

1.oldCap > 0,此时说明在这之前已经调用过resize至少一次

​ (1)oldCap >= MAXIMUM_CAPACITY,容量达到最大值,更新阈值为最大值,返回当前数组,不再扩容

​ (2)否则,newCap = oldCap << 1、newThr = oldThr << 1,容量和阈值均扩大为原来的2倍

2.oldCap <= 0,且oldThr(threshold) > 0,对应初始化map时调用resize的情况。

​ (1)此时oldThr的值等于 threshold,是通过 tableSizeFor 方法得到的一个2的n次幂的值。因此,需要把 oldThr 的值,也就是 threshold ,赋值给新数组的容量 newCap,以保证数组的容量是2的n次幂。

​ (2)计算newThr为容量 * 加载因子,并将它赋值给threshold,用来表示下次需要扩容的阈值

3.oldCap和oldThr均为负数,说明是由无参构造创建的,容量和阈值直接采用默认值

数组内容的变化

1.根据扩大的容量和阈值创建新数组

2.遍历旧数组,若当前元素的next为空,则说明当前位置只有一个元素,无链表/红黑树,重新计算位置e.hash & (newCap - 1),放到新数组中即可

3.如果是红黑树结构,则拆分红黑树,必要时有可能退化成链表

4.如果是链表,则需要计算并判断当前位置的链表是否需要移动到新的位置

​ (1)如果当前元素的hash值和oldCap做与运算为0,则原位置不变,数组下标不变

​ (2)否则,需要移动到新的位置,数组下标为原下标 + 旧数组容量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ConnorYan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值