栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Connor学Java - HashMap扩容机制

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Connor学Java - HashMap扩容机制

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[] resize() {
    Node[] 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[] newTab = (Node[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
    	for (int j = 0; j < oldCap; ++j) {
        	Node 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)e).split(this, newTab, j, oldCap);
                // 4
                else { // preserve order
                    Node loHead = null, loTail = null;
                    Node hiHead = null, hiTail = null;
                    Node 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[] 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)否则,需要移动到新的位置,数组下标为原下标 + 旧数组容量

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1009730.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号