tableSizeFor(int cap)方法返回不小于指定参数cap的最小2的整数次幂,具体是怎么实现的呢?看源码!

    /**
     * Returns a power of two size for the given target capacity.
     */
    static final int tableSizeFor(int cap) {
        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;
    }

1)为什么cap要减一?

2)|=是什么?

3)>>>又是什么?

4)为什么要返回n+1

先解释一下"|"是什么

这里的“|”是叫做位或运算,百度百科这样解释到:按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的2个二进位有一个为1时,结果位就为1。当参与运算的是负数时,参与两个数均以补码出现。

假设使用8位来储存数字,下面用一张图来说明下位或运算: 8|1

JDK1.8_HashMap源码__tableSizeFor方法解析-LMLPHP

十进制8的二进制表示为图中的A : 00001000

十进制1的二进制表示为图中的B : 00000001

从图中可以看出A从右向左第四个位为1,B从右向左第一个位为1,其余位都为0,因此结果的第一个和第四个位都为1,结果就是00001001转换为十进制就是9,因此 8 | 1 = 9 。

A |= B 的意思就是 A = A |  B

>>>表示无符号右移

也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。

假设使用8位来储存数字,下面用一张图来说明下右移运算: 8>>>1

JDK1.8_HashMap源码__tableSizeFor方法解析-LMLPHP

8>>>1表示将8右移一位,如图所示:右移后高位也就是蓝色部分补0,低位也就是红色部分直接舍弃,结果就变成了0000100转换为十进制就是4,因此 8 >>> 1 = 4

 明白了>>>和 | 的作用就可以来解释上面的源码了,

假设cap=9,n = cap - 1 = 8( 至于为什么需要减一待会儿再说)

n |= n >>> 1; 也就是 n = n | (n>>>1)
在java中int占4个字节也就是32位,因此8的二进制表示为:
00000000......00001000 中间省略16个0
00000000......00000100 8>>>1
00000000......00001100 8 |= 8>>>1 结果转换为十进制为:12

00000000......00000011 12>>>2
00000000......00001111 12 |= 12>>>1 结果转化为十进制为:15

00000000......00000000 15>>>4
00000000......00001111 15 |= 15>>>4 结果转化为十进制为:15

00000000......00000000 15>>>8
00000000......00001111 15 |= 15>>>8 结果转化为十进制为:15  

00000000......00000000 15>>>16
00000000......00001111 15 |= 15>>>16 结果转化为十进制为:15  

最后返回 n+1 也就是 16


上面的操作其实就是把 8 的二进制表示 00000000......00001000,把 1 后面的 0 全部变成了000000000......0001111.

为什么 n+1 之后就是2的整数次幂呢?

看一下二进制是如何转换为十进制的就明白了

二进制转换为十进制:

设一个n位的二进制的位数从右至左依次为0~n位,那么从0~n位分别乘于2的n次幂的和即为该二进制的十进制形式。

00000001 的十进制表示为 1 * 2^0 = 1

00000001 加一之后就变成了 00000010 转换为十进制就是 0*2^0 + 1*2^1 = 0 + 2 = 2

00000011 的十进制表示为 1*2^0 + 1*2^1 = 3

00000011 加一之后就变成了00000100 转换为十进制就是 0*2^0 + 0*2^1 + 2*2^2 = 0 + 0 + 4 = 4

00000111 的十进制表示为 1*2^0 + 1*2^1 + 1*2^2 = 7

00000111加一之后就变成了00001000 转换为十进制就是 0*2^0 + 0*2^1 + 0*2^2 + 1*2^3 + = 0 + 0 + 0 + 8 = 8

为什么要对cap进行减一操作呢?

这样就可以避免当cap已经是2的整数次幂时,再对cap进行一次求次幂操作,比如:cap=16,如果没有减一结果就会变成32,而16已经符合HashMap的要求了.

                                                     
07-23 17:26