/**
* 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;
}
趁着周五下班前的一点时间我又复习了一遍HashMap的源码
注意到了这个位运算方法,这个算法的目的是求出大于等于给定数字的最小二次幂。之前好像群里有人把这道题提到leetcode上去了,今天看到了就了解了一下。
第一眼看上去,很神奇,怎么就几个位运算就求出了最小二次幂呢。。。。
然后思考。。。不得其解,与群友讨论,有人给出了简略答案,答案我都看了好几遍才看懂。。。
首先我们只看五次位运算:
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
ps:这里的>>>指的是无符号右移,就是最高位符号位一起移动(因为HashMap的大小已经先判断过了 肯定是正数)
首先我们假设一个数的二进制表示是:
Step1
n | 0..01...(肯定有一个最高非0位,n==0的情况我后面会提到) |
n>>>1 | 0..001... |
n=n|n>>>1 | 0..011... |
这里的作用实际上就是把最高非0位向右扩散成了两位
Step2
n | 0..011...(从“最高非零位”开始2位是1) |
n>>>2 | 0..00011... |
n=n|n>>>2 | 0..01111... |
把最高非0位向右扩散成了四位
Step3
n | 0..01111...(从“最高非零位”开始4位是1) |
n>>>4 | 0..000001111... |
n=n|n>>>4 | 0..011111111... |
把最高位的1向右扩散成了八位
....
Step5
n | 0..01111111111111111... |
n>>>16 | 0..000000000000000001111111111111111... |
n=n|n>>>16 | 0..011111111111111111111111111111111...(32个1) |
忽略无符号int是32位的因素 最后得到的二进制就是这样
最高非0位前面的0始终没有收到影响,最高非0位向后扩散成了32个1,而右移操作如果移超出32位的都会被舍弃。
所以最后得到的就是:最开始的n从最高非0位后面的位数全变成1。
如(21)10101变成了11111变成了31
最后再加1就让所有的1变成0,最高非0位往前移动1位,得到了最小二次幂。
(31+1=32是21的最小二次幂)
到这这个方法就差不多分析完了,还剩下几个小问题没解决:
1.最开始的n-1
最开始的n-1应该是为了出现本身n就是最小二次幂,如果不减1就会出现(4的最小二次幂是8)。
特殊的:n=1时,n-1=0没有1传播,传播的就是0,最后0+1=1也是最小二次幂
2.什么情况下位移之后的n会小于0呢
在HashMap中,n肯定是大于0的正整数,理论上来说不可能小于0呀,甚至说就算n小于0,无符号右移是会把最高位的负数位(1)右移成数据位的吧,最高位肯定是0呀,难道写小于0是习惯性的排除无法想象的情况?
希望知道问题二的朋友能够留言告诉我,谢谢