背景
在HashMap
源码中有过这么一段代码
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;
}
如果你不会位运算的话,这段代码基本上是看不懂的。
计算机在底层使用的是二进制补码进行运算。对应的二进制位进行操作,计算机只识别0和1 。
巧妙的使用位运算可以大量减少运行开销,优化算法。
位运算符与赋值运算符结合,组成新的复合赋值运算符,它们是:
&=
例:a&=b
相当于 a=a&b
|=
例:a|=b
相当于 a=a|b
>>=
例:a>>=b
相当于 a=a>>b
<<=
例:a<<=b
相当于 a=a<<b
^=
例:a^=b
相当于 a=a^b
不同长度的数据进行位运算
如果两个不同长度的数据进行位运算时,系统会将二者按右端对齐,然后进行位运算。
以“与运算”为例说明如下:
我们知道在 C 语言中 long 型占 4 个字节,int 型占 2 个字节,如果一个 long 型数据与一个 int 型数据进行“与运算”,右端对齐后,左边不足的位依下面三种情况补足。
回到本文最前面的那段代码
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;
}
参数 cap 的值肯定大于 0,故 n 大于等于 0 ,假设 n = 0,经过右移之后,依旧为 0 ,0 与 0 异或依旧为 0 ,通过 return 语句的 n+ 1 计算得 1,即 2 的 0 次幂。
当 n 大于 0 时,n 的二进制位肯定会有位的值为 1,即 001xx..xx 的形式,接着,对 n 右移 1 位得 0001xx..xx,再进行位或,由于 1 与 0 或 1 异或结果都为 1,所以结果必为 0011xx..xx 的形式。以此类推,位移 2 位、4 位、8 位、 16 位,最终该算法能让最高位的 1 后面的所有位全变为 1。
下面用一张图举例说明一下:
该算法的最后再让结果值 n + 1,所得即为 2 的整数次幂。为了让结果使结果大于等于参数 cap,在算法开始时,令 cap - 1。