美文网首页
HashMap和ConcurrentHashMap中关于求最小的

HashMap和ConcurrentHashMap中关于求最小的

作者: 赵荆州 | 来源:发表于2018-07-24 11:26 被阅读24次

    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是怎么来的呢?

        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;
    

    嗯哼?第一眼看到觉得莫名奇妙,仔细看看,就会很佩服了!
    这里主要用到了两个运算,位移运算(向右无符号,高位补零)和或运算。
    例如:cap=3,套入上面的公式,如下:

      int n = 3-1;
      //第一步位移运算转换为二进制为:10>>>1  = 01
      //第二部或运算转换为二进制为:10|=01  = 11
      n|=n>>>1;
    

    最终n=11(二进制)最后加上1 = 100(二进制)
    转换成10进制就是4啦!
    需要注意的是:cap-1,这是因为,如果cap本身就是2的次方,套用上面的公式得出来的结果就不是最小的2的n次方了。

    下面来说ConcurrentHashMap中的最小2的n次方技巧。应该都知道ConcurrentHashMap是通过分段锁来提高效率的,其中一个segment就是一个锁。但是初始化的时候初始segment数组多大合适呢?
    segment大小跟concurrencyLevel有关,求的是大于concurencyLevel的最小2的n次方。

     int ssize = 1;
     while (ssize < concurrencyLevel) {
        ++ sshift; 
        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中关于求最小的

          本文链接:https://www.haomeiwen.com/subject/izhemftx.html