HashMap的扩容时机和初始化集合容量时是否根据公式计算

本文深入探讨了集合初始化时指定初始值大小的正确性,通过分析HashMap源码,揭示了为何直接指定集合大小而非计算是更优的选择。理解这一点对于避免不必要的内存浪费和提升程序性能至关重要。

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

阿里巴巴Java开发手册中提到:集合初始化时,指定集合初始值大小。
对于集合的初始化大小,网上流传两种观点:

观点对错
初始化大小 = (元素个数 / 负载因子) + 1错误
直接指定集合大小正确

为什么第二个观点正确?我们断点调试一下寻找答案。

扩容相关的属性:

/**
 * 底层数据结构:节点数组
 */
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
/**
 * 当前元素的个数
 */
transient int size;
/**
* 临界值,创建集合时,如果指定集合大小为 5,那么 threshold = 5,否则默认值为 16,详见
* public HashMap(int initialCapacity, float loadFactor) 构造器
*/
int threshold;

调试代码如下

HashMap<Integer, Integer> map = new HashMap<>(5);
for (int i = 0; i < 5; i++) {
    map.put(i, i);    // @1
}

@1处打断点,进入put方法,发现keyvalue不是我们预期的值,keyjava.security.ProtectionDomain对象,valueObject对象,我们在@2处打断点,直接跳到下一个断点处,此时又跳到了@2的断点处,这时候的keyvalue才是我们预期的值。

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {    // @2
        inflateTable(threshold);
    }
    ... 省略了部分代码
    addEntry(hash, key, value, i);    // @3
    return null;
}

第一次调用put方法是由JVM主动调用的,并且只会调用,之后不管你创建多少个Map,调用了多少次put方法,JVM都不再调用。
第二次调用时,if (table == EMPTY_TABLE)为真,进入inflateTable方法,参数为threshold,创建集合时 threshold 的默认值为16,如果指定了集合大小,threshold值为集合大小。

private void inflateTable(int toSize) {
    int capacity = roundUpToPowerOf2(toSize);    // @4
    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);    // @5
    table = new Entry[capacity];    // @6
    ... 省略了部分代码
}

断点@4是计算容器的真实容量,例如我们指定集合大小为5,那么真实容量为8。真实容量 X 的规则:

  1. X > 5
  2. X 是 2 的次幂,并且是第一个大于 5 的数

断点@5是计算下次扩容的 threshold 的临界值,threshold = 6

断点@6初始化容器 table

我们再回到断点@3 addEntry 方法,此处是将数据存储到 table 数组中

void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {    // @7
        resize(2 * table.length);
        ... 省略了部分代码
    }
    createEntry(hash, key, value, bucketIndex);
}

断点@7处校验是否达到扩容的条件,当且仅当满足下面这两条,才需要扩容

  1. 当前元素个数 size 大于临界值 threshold,在断点@5处计算的threshold = 6
  2. table[bucketIndex] 不为 nullbucketIndex是元素key计算的数组下标

由上可知,观点二是正确的,即:集合初始化时,指定集合初始值大小,不需要手动计算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值