Java开发手册中为什么建议初始化HashMap的容量大小,以及HashMap源码中相关参数(容量大小设置参数公式)说明

发布时间 2023-06-05 18:02:50作者: 霸道流氓

场景

Java开发手册中对于HashMap的推荐如下:

【推荐】集合初始化时,指定集合初始值大小。

说明:HashMap 使用 HashMap(int initialCapacity) 初始化,如果暂时无法确定集合大小,那么指定默认值(16)即可。

正例:

initialCapacity = (需要存储的元素个数 / 负载因子) + 1。

注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。

反例:

HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素增加而被迫不断扩容,

resize()方法总共会调用 8 次,反复重建哈希表和数据迁移。

当放置的集合元素个数达千万级时会影响程序性能。

 

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

通过查看HashMap的源码,先看下其主要的成员变量

 

1、transient int size; 

记录了 Map 中 KV 对的个数。

2、final float loadFactor; 

装载因子,用来衡量 HashMap 满的程度。

loadFactor 的默认值为 0.75f  

static final float DEFAULT_LOAD_FACTOR = 0.75f;

3、int threshold;  临界值,当实际 KV 个数超过 threshold 时,HashMap 会将容量扩容,threshold =容量 * 加载因子。

4、除了以上成员变量,还有一个概念capacity,容量,如果不指定,默认容量是16。

其源码声明为

        /**
         * The default initial capacity - MUST be a power of two.
         */
        //static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

 

size指当前已经有多少元素,capacity指最多可以装多少元素。

5、默认情况下 HashMap 的容量是 16,但是,如果用户通过构造函数指定了一个数字作为容量,

那么 Hash 会选择大于该数字的第一个 2 的幂作为容量。

在初始化 HashMap 的时候,应该尽量指定其大小。尤其是当你已知 map 中存放的元素个数时。(《阿里巴巴 Java 开发规约》)

HashMap 的扩容条件就是当 HashMap 中的元素个数(size)超过临界值(threshold)时就会自动扩容。

在 HashMap 中,threshold = loadFactor * capacity。loadFactor 是装载因子,

表示 HashMap 满的程度,默认值为 0.75f,设置成0.75 有一个好处,那就是 0.75 正好是 3/4,而 capacity 又是 2 的幂。

所以,两个数的乘积都是整数。对于一个默认的 HashMap 来说,默认情况下,当其 size 大于 12(16*0.75) 时就会触发扩容。

为了验证初始化容量后的性能要高于默认容量,编写以下测试代码

        int count = 10000000;
        Map<Integer, Integer> map1 = new HashMap<>();

        try(Cost cost = new Cost()){
            for (int i = 0; i < count; i++) {
                map1.put(i,i);
            }
        }

        Map<Integer, Integer> map2 = new HashMap<Integer, Integer>((int) (count / 0.75 +1));

        try(Cost cost = new Cost()){
            for (int i = 0; i < count; i++) {
                map2.put(i,i);
            }
        }

注意这里的代码耗时统计的方式可以参考

Java实战-基于JDK的LRU算法实现、优雅的实现代码耗时统计(Spring AOP、AutoCloseable方式):

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/130970242

以上对比结果如下

从结果中可知,在已知HashMap中将要存放的KV个数的时候,设置一个合理的初始化容量可以有效的提高性能。

如果我们没有设置初始容量大小,随着元素的不断增加,HashMap 会发生多次扩容,

而 HashMap 中的扩容机制决定了每次扩容都需要重建 hash 表,是非常影响性能的。