HashMap和ConcurrentHashMap中的initialCapacity
HashMap 默认有一个初始大小(initialCapacity),这个初始大小是16。
在各种开发规范手册中都可以看到会建议设置这个大小。
例如:new HashMap(3);
那么我们设置了初始容量为3,HashMap的容量真的会初始化为3了吗?
答案是否定的。
为了提高Hash效率,Java中会重新计算这个值,获得一个>3的最小2的N次方,大于3的最小2的N次方就是4(关于为什么请参照
https://www.zhihu.com/question/28562088/answer/111668116)。
这个4是怎么来的呢?
1 | int n = cap - 1; |
嗯哼?第一眼看到觉得莫名奇妙,仔细看看,就会很佩服了!
这里主要用到了两个运算,位移运算(向右无符号,高位补零)和或运算。
例如:cap=3,套入上面的公式,如下:
1 | int n = 3-1; |
最终n=11(二进制)最后加上1 = 100(二进制)
转换成10进制就是4啦!
需要注意的是:cap-1,这是因为,如果cap本身就是2的次方,套用上面的公式得出来的结果就不是最小的2的n次方了。
下面来说ConcurrentHashMap中的最小2的n次方技巧。应该都知道ConcurrentHashMap是通过分段锁来提高效率的,其中一个segment就是一个锁。但是初始化的时候初始segment数组多大合适呢?
segment大小跟concurrencyLevel有关,求的是大于concurencyLevel的最小2的n次方。
1 | int ssize = 1; |
这里用到的是位移运算(向左)。
例如concurrencyLevel=5,
第一次
ssize<<=1 结果:10(二进制),2(10进制)
第二次
ssize<<=1 结果:100(二进制),4(10进制)
第三次
ssize<<=1 结果:1000(二进制),8(10进制)
这时结果已经>concurrencyLevel,while结束,于是最终结果就是8.
所以最终segment 数组大小会被初始化为8.
HashMap和ConcurrentHashMap中的initialCapacity