Java位运算符
今天在学习 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);
}
方法的作用就是,输入一个数,如果该数为2的幂次方,那么将原数返回回去,如果不是,那么会返回另一个数,也会是2的幂次方。看完我就觉得我该好好看看位运算符了。
位运算符主要针对二进制进行运算,它包括了:“与”、“非”、“或”、“异或”、“左移”、“右移”和“无符号右移”,下面详细介绍每个位运算符。
使用的例子为:a = 60,b = 13;它们的二进制格式表示将如下:
A = 0011 1100
B = 0000 1101
-----------------
A&b = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A= 1100 0011
“与”运算
“与”运算对应的符号是 “&”,如果相对应位都是1,则结果为1,否则为0
“或”运算
“或”运算对应的符号是 “|”,如果相对应位都是0,则结果为0,否则为1
“非”运算
“非”运算对应的符号是 “~”,按位取反运算符翻转操作数的每一位,即0变成1,1变成0
“异或”运算
“异或”运算对应的符号是 “^”,如果相对应位值相同,则结果为0,否则为1
“左移”运算
“左移”运算对应的符号是:”<<”,左操作数按位左移右操作数指定的位数。比如A << 2得到240,即 1111 0000,实际上在不超出数据范围的情况下,这种操作相当于左操作数乘以2的右操作数次幂,对于负数也依然成立。
“右移”运算
“右移”运算对应的符号是:”>>”,左操作数按位右移右操作数指定的位数,比如A >> 2得到15即 1111,实际上在不超出数据范围的情况下,这种操作相当于左操作数除以2的右操作数次幂。
如果左操作数是正数,运算相当于对2的右操作数次幂进行取模运算,如:7>>2=1;9>>10=0
如果左操作数是负数,情况不太意义,如:-7>>2=-2
“无符号右移”运算
“无符号右移”运算对应的符号是:”>>>”,按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充,是针对无符号数的。
Java集合中设定容量大小的算法
回到我们最开始的那个算法上来。
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=8,则n初始化为7,7的二进制表示为 0111 (为了方便,省去很多不必要的0)后面的5个步骤为
0111 | 0011 = 0111 (移1位得到有操作数)
0111 | 0001 = 0111 (移2位得到有操作数)
0111 | 0000 = 0111 (移4位得到有操作数)
0111 | 0000 = 0111 (移8位得到有操作数)
0111 | 0000 = 0111 (移16位得到有操作数)
所以我们可以看出,当输入2的幂次方的数时,我们对其减一,得到的结果的二进制表示为最高位位为0,后面全为1,将这个二进制数再与自己无符号右移得到的结果进行或运算,都是还是等于自己的,再将结果加一,还是变回原样。
如果输入 cap=65,则n初始化为64,64的二进制表示为 1000000,后面的5个步骤为
1000000 | 0100000 = 1100000(移1位得到有操作数)
1100000 | 0011000 = 1111000(移2位得到有操作数)
1111000 | 0000111 = 1111111(移4位得到有操作数)
1111111 | 0000000 = 1111111(移8位得到有操作数)
1111111 | 0000000 = 1111111(移16位得到有操作数)
可以看出最后得到的二进制数都是全1的,因为 int 类型是32位的,所以这种操作,最后都能全1的二进制数,至于是多少位,依据输入数最高位所在的位判断,而这种二进制数加一之后得到的值肯定是2的幂次方,不得不说这种算法简洁而又高效,学习了!
判断数是否为2的幂次方
还有一种算法来判断一个数是否为2的幂次方
public static boolean isPowerOf2(int n) {
return (n & -n) == n;
}
这个道理差不多,也是进行位运算,算法很高效。如果输入的是65,则其二进制表示为 1000001,而-65则是 11111111111111111111111110111111 (负数的补码),再按位与,得到结果为1,和原来的不一样,所以不是2的整数次方数。