https://www.hollischuang.com/archives/3545
摘要,占据为默认0.75的总容量后扩容。
当然,以上结论也是有理论支撑的。我们HashMap中傻傻分不清楚的那些概念文章介绍过,HashMap有扩容机制,就是当达到扩容条件时会进行扩容。HashMap的扩容条件就是当HashMap中的元素个数(size)超过临界值(threshold)时就会自动扩容。在HashMap中,threshold = loadFactor * capacity
。
所以,如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容,而HashMap中的扩容机制决定了每次扩容都需要重建hash表,是非常影响性能的。
从上面的代码示例中,我们还发现,同样是设置初始化容量,设置的数值不同也会影响性能,那么当我们已知HashMap中即将存放的KV个数的时候,容量设置成多少为好呢?
在jdk1.7中,初始化容量设置成1的时候,输出结果是2。在jdk1.8中,如果我们传入的初始化容量为1,实际上设置的结果也为1,上面代码输出结果为2的原因是代码中map.put(“hahaha”, “hollischuang”);导致了扩容,容量从1扩容到2。
那么,话题再说回来,当我们通过HashMap(int initialCapacity)设置初始容量的时候,HashMap并不一定会直接采用我们传入的数值,而是经过计算,得到一个新值,目的是提高hash的效率。(1->1、3->4、7->8、9->16)
在Jdk 1.7和Jdk 1.8中,HashMap初始化这个容量的时机不同。
jdk1.8中,在调用HashMap的构造函数定义HashMap的时候,就会进行容量的设定。
而在Jdk 1.7中,要等到第一次put操作时才进行这一操作。
=======================================================================================>
方案1:
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
代码debug:
4->4
5->8
6->8
7->8
8->8
方案2:
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
代码debug:
4->8
5->8
6->8
7->8
8->16
=======================================================================================>
newHashMapWithExpectedSize
(需要存储的元素/负载因子)+1
(int) ((float) expectedSize / 0.75F + 1.0F);
阿里巴巴的规范指定,guava原创,参考的是JDK8中putAll.
当HashMap内部维护的哈希表的容量达到75%时(默认情况下),会触发rehash,而rehash的过程是比较耗费时间的。所以初始化容量要设置成expectedSize/0.75 + 1的话,可以有效的减少冲突也可以减小误差。(如果数量始终一致,不用refresh)
(空间换时间的思想)
比如线程方面:
锁->时间换空间
threadLocal->空间换时间