1. HashMap的容量与性能
HashMap的性能受到两个参数的影响:初始化容量和负载因子,下面来详细讲述这几个关键问题。
1.1 Initial Capacity与Load Factor
- Initial Capacity:初始化容量,它表示HashMap底层的那个数组,也就是Entry数组有多长,这个值默认是16。
- Load Factor:负载因子,它表示HashMap的负载程度,换句话说,它表示HashMap到底有多满了,是不是需要扩容了,这个值默认是0.75f。
初始化容量和负载因子的默认值是Java官方经过实践和优化得到的数据,可以适应大多数的场景。
当然也可以不使用其默认值,可以在构造的时候,自定义HashMap的容量和负载因子:
//单纯指定容量
Map<String,String> hashMapWithCapacity=new HashMap<>(64);
//自定义容量和负载因子
Map<String,String> hashMapWithCapacityAndLF=new HashMap<>(64, 0.9f);
一旦需要自定义容量和负载因子,我们需要搞清楚,他们到底是什么,用来干嘛,会对性能造成什么影响。
一句话:这两个数值会影响到HashMap的扩容,而扩容是一个对性能影响非常大的操作。
1.2 HashMap的扩容
1.2.1 扩容是什么
HashMap的扩容:HashMap被初始化之后,其容量是有限的(可以是默认,也可以是自定义的),当元素不断被插入,是HashMap达到一定的程度(负载因子决定),这个时候,HashMap就会扩容。
根据源码,使用公式表示,是否扩容由容量和负载因子的乘积决定!!!!
触发扩容的条件:
HashMap.Size >= Capacity * LoadFactor
- HashMap.Size:当前HashMap的实际元素个数
- Capacity:容量
- LoadFactor:负载因子
如果在默认值的条件下:
HashMap.Size = 16 * 0.75 = 12
也就是,默认的情况下,插入十二个元素的时候,就会触发扩容。
1.2.2 扩容的步骤
一旦HashMap的size超过了Capacity * LoadFactor乘积,就会触发扩容,那如何扩容呢?,需要经过两步:
- resize:即:创建一个new Entry数组,长度是原来old Entry的2倍。
- rehash:遍历old Entry数组,把里面的每一个元素取出来重新计算hash和index。为什么要重新计算呢?想一想之前的index计算公式:
index = hash值 & (length - 1)
对,因为长度已经改了,所以index肯定会不一样,举个例子:
- 当原数组长度为16时,Hash运算是和1111做与运算;
- 新数组长度为32,Hash运算是和11111做与运算。
Hash结果显然不同。
1.3 容量与性能(常见问题)
综上所述,不同的容量和负载因子的设置,会导致hashmap的扩容频率不一致。所以:
- Initial Capacity设置得低,存储空间少,但是增加了rehash的频率。所以如果能够预期到会存储比较多的数据,那么这个值应该设置高一点
- Initial Capacity设置得高,存储空间多,rehash频率少。但是会增加迭代的成本。
所以总结一个场景
- Initial Capacity设置高:大量存储,少量迭代
- Initial Capacity设置低:数据少,迭代频繁
常见问题:
- 如何扩容的
- 初始化容量是多少,如何设置,有什么应用场景
- 负载因子的作用是什么
欢迎大家一起交流!