集合初始化的时候,建议指定集合初始化值大小。
说明:HashMap使用HashMap(int initialCapacity) 初始化。
正例:initialCapacity=(需要存储的元素个数/负载因子)+1 。
initialCapacity = (int) ((float) expectedSize / 0.75F + 1.0F)
注意负载因子(即loaderfactor) 默认为0.75,如果暂时无法确定初始值大小,请设置为16(即默认值)。
反例:HashMap需要放置1024个元素,由于没有设置容量初始大小,随着元素不断增加,容量7次被迫扩大,resize需要重建hash表,严重影响性能。
在jdk中,当我们new HashMap并且指定初始化容量capacity时,jdk会帮我们取第一个大于capacity的2次幂。
具体的实现是:
- 先把capacity - 1
- 进行多次无符号右移和或运算
- 最后 + 1
其中1,2,3步作用是将初始容量变成2次幂,比如初始容量是3将变成4,初始容量为5将变成8,也就是得到最近且大于初始容量的2次幂的值,其中得到的2次幂就是Map对象实际容量大小,
实际容量大小一定是2次幂。
比如:
- 新建一个Map,事先知道只存储一个元素,那么它的初始容量可以设置为2,put一个元素不会扩容,如果设置为1,put一个元素将会扩容一次。
- 如果事先知道只存储2个元素,那么它的初始容量可以为3,也可以为4,因为3,4初始容量会转化为最近的2次幂,也就是4.
- 如果事先知道只存储3个元素,那么它的初始容量可以设置为5,6,7,8
综上可知:
初始容量可以设置为:(int) ((float) expectedSize / 0.75F + 1.0F) 到最近2次幂之间的数值。
注意点:
HashMap 中更为危险的是,HashMap 在并发执行扩容操作时会引起死循环,导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Entry 链表形成环形数据结构,一旦形成环形数据结构,Entry 的 next 节点永远不为空,就会在获取 Entry 时产生死循环。形成环形链表的原因主要跟 JDK1.7 下 HashMap 发生碰撞后,链表是前序插入有关。