HashMap源码-扩容resize方法

环境:jdk1.8

resize方法源码如下
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.判断旧表容量值是否大于0
    if (oldCap > 0) 
    {
    	//判断旧表容量值是否大于最大容量值,如果大于或等于,则阈值取:2<<30,且返回旧表,这里相当于只是调整了阈值大小,没有对旧表大小进行实际改变
        if (oldCap >= MAXIMUM_CAPACITY) 
        {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        //初始化新容量值为旧容量值的2倍,然后与最大容量值比较,当新容量值小于最大容量值,且旧容量值大于等于默认初始化容量值,这时也对新阈值赋值为旧阈值的2倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    //2.这里的前提条件是oldCap=0,说明这是第一次创建表,将对新容量赋值为旧阈值,这里旧阈值其实就等于HashMap的初始化容量值
    else if (oldThr > 0)
        newCap = oldThr;
    //3.当oldCap=0且oldThr=0的情况下,我们默认对新容量值和新阈值按初始化值进行赋值
    else 
    {
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    //当进入判断条件2时,这时新阈值就会等于0
    if (newThr == 0) 
    {
    	//根据新容量值乘以加载因子得到新阈值
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    //修改全局变量threshold的值
    threshold = newThr;
	//定义并初始化新表,指定容量
    @SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    //修改全局变量table的值
    table = newTab;
    //判断旧表是否存在,如果存在,则需要将旧表中的数据复制到新表中
    if (oldTab != null) 
    {
    	//遍历旧表
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            //判断旧表中的每个位置上的元素是否存在,将该元素赋值给临时变量e
            if ((e = oldTab[j]) != null) 
            {
            	//将旧表元素置空,有利于GC
                oldTab[j] = null;
                //因为HashMap的数据结构是数组+链表,这里需要判断该元素所在链表是否有下个元素,如果没有,则可以直接将该元素放入新表中
                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 
                {
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                    	//逐个取出链表中的元素
                        next = e.next;
                        //比较hash值,这里的目的是为了处理hash冲突,使用hiHead存储哈希冲突的元素
                        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;
}

总结
1、扩容操作首先主要的操作是给新表的容量以及阈值正确赋值
2、创建新表后,要判断节点元素是红黑树还是链表,分别做不同的处理
3、处理链表元素时要注意哈希冲突的元素要单独提取出来存储


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMapresize()方法是用于扩容HashMap方法。当HashMap中存储的数据量大于threshold时或进行初始化HashMap时,会触发resize()方法进行扩容操作。\[1\] 在HashMap的putVal()方法中,会先判断table是否为空,如果为空,则会执行resize()方法进行初始化table。\[1\] 在HashMap中,当存储的数据量大于threshold时,也会执行resize()方法进行扩容操作。\[1\] 在JDK1.8之前,扩容操作在多线程情况下容易造成环形链表,可能导致get操作产生死循环。而在JDK1.8中,resize()方法不再调用transfer()方法,而是直接将原来transfer()方法中的代码写在自己的方法体内。此外,扩容后新数组中的链表顺序与旧数组中的链表顺序保持一致,不再改变顺序。\[2\] #### 引用[.reference_title] - *1* [最详细HashMap集合源码讲解(resize()方法)](https://blog.csdn.net/weixin_37541878/article/details/119391236)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [HashMapresize方法](https://blog.csdn.net/qq_38304320/article/details/103496039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值