问题描述
获取比a大,最接近a的2的次方的数-(最小二次幂) 例子:
a = 5, 返回 8;
a = 13, 返回 16;
a = 1021, 返回 1024;
位运算知识储备
**1.左移**
\->左移运算符“<
->左移m<
**2.右移**
右移运算符“>>” \- 使指定值的所有位都右移规定的次数。
右移m>>n 代表把数字m除以2的n次方,原来是正数的还是正数,负数还是负数。
注意,如果是单数,也就是二进制末位为1,则结果是将m除以2的n次方的整数商。
**3.无符号右移**
无符号右移运算符“>>>” \- 同右移,但是结果全变正数。
方法1:右移,找到 最高非0位 为第几位
static final int tableSizeFor1(int cap) {
int n = cap >> 1;
int length = 0;
while (n != 0){
n = n >> 1;
length ++;
}
return 2 << length;
}
方法2:
static final int tableSizeFor3(int cap) {
int length = 1;
while (length < cap) {
length <<= 1;
}
return length;
}
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 + 1;
}
简单说明:
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
//cap-1后,n的二进制最右一位肯定和cap的最右一位不同,即一个为0,一个为1,例如cap=17(00010001),n=cap-1=16(00010000)
int n = cap - 1;
//n = (00010000 | 00001000) = 00011000
n |= n >>> 1;
//n = (00011000 | 00000110) = 00011110
n |= n >>> 2;
//n = (00011110 | 00000001) = 00011111
n |= n >>> 4;
//n = (00011111 | 00000000) = 00011111
n |= n >>> 8;
//n = (00011111 | 00000000) = 00011111
n |= n >>> 16;
//n = 00011111 = 31
//n = 31 + 1 = 32, 即最终的cap = 32 = 2 的 (n=5)次方
return n + 1;
}
为什么右移到16位,可以得到的最大值是32个1 ? 11111111 11111111 11111111 11111111
这个是因为java的int类型用4个字节32位来进行存储的,再往后也没有意义了
int类型占4个字节(byte);
一个字节=8bit(位);
一个int类型的数值占32bit(位)int i = 123;
10进制123转为二进制后等于:1111011 。完整补位后:00000000 00000000 00000000 01111011
循环测试后,该方案最佳(不愧是jdk自带的)
其他:
求小于等于2的幂次方的数:
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 - (n >> 1);
}
public static void main(String[] args) {
System.out.println(tableSizeFor(31));
System.out.println(tableSizeFor(34));
}
输出:
16
32
参考链接: