为了方便说明,这里明确几个名词:

    • capacity 即容量,默认16。
    • loadFactor 加载因子,默认是0.75
    • threshold 阈值。阈值=容量*加载因子。默认12。当元素数量超过阈值时便会触发扩容。


    什么时候触发扩容?
    一般情况下,当元素数量超过阈值时便会触发扩容。每次扩容的容量都是之前容量的2倍。
    HashMap的容量是有上限的,必须小于1<<30,即1073741824。如果容量超出了这个数,则不再增长,且阈值会被设置为Integer.MAX_VALUEHashMap的扩容机制 - 图1,即永远不会超出阈值了)。

    JDK7中的扩容机制
    JDK7的扩容机制相对简单,有以下特性:

    • 空参数的构造函数:以默认容量、默认负载因子、默认阈值初始化数组。内部数组是空数组
    • 有参构造函数:根据参数确定容量、负载因子、阈值等。
    • 第一次put时会初始化数组,其容量变为不小于指定容量的2的幂数。然后根据负载因子确定阈值。
    • 如果不是第一次扩容,则HashMap的扩容机制 - 图2HashMap的扩容机制 - 图3

    JDK8的扩容机制
    JDK8的扩容做了许多调整。
    HashMap的容量变化通常存在以下几种情况:

    1. 空参数的构造函数:实例化的HashMap默认内部数组是null,即没有实例化。第一次调用put方法时,则会开始第一次初始化扩容,长度为16。
    2. 有参构造函数:用于指定容量。会根据指定的正整数找到不小于指定容量的2的幂数,将这个数设置赋值给阈值(threshold)。第一次调用put方法时,会将阈值赋值给容量,然后让 HashMap的扩容机制 - 图4。(因此并不是我们手动指定了容量就一定不会触发扩容,超过阈值后一样会扩容!!)
    3. 如果不是第一次扩容,则容量变为原来的2倍,阈值也变为原来的2倍。(容量和阈值都变为原来的2倍时,负载因子还是不变)

    此外还有几个细节需要注意:

    • 首次put时,先会触发扩容(算是初始化),然后存入数据,然后判断是否需要扩容;
    • 不是首次put,则不再初始化,直接存入数据,然后判断是否需要扩容;