bitCount源码解析
因为是在idea里面写的文章,格式化的时候 用 ```java 标签写内容 ,格式化会把缩进搞没了,还是用 ```text标签
每组xx bit,意思是例如每组8bit , 代表一组里面左右部分都是4bit . 要合并左右部分的bit1位数的意思
源码
public static int bitCount(int i){
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
代码的思路
最直观的思路就是第二行 , 也就是每组4bit , 合并左右两个bit位的bit1位数时 .
代码是:
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
例如有每组2n个bit , 要把 左边和右边的bit1位数相加 . 那么就是 (i & mask) + (i >>> n) & mask .
也就是源代码变成这样也是可以得出结果的
int[] arr = new int[]{0x55555555, 0x33333333, 0x0f0f0f0f, 0x00ff00ff, 0x0000ffff};
for (int b = 0; b < 5; b++) {
i = (i & arr[b]) + ((i >>> (1 << b)) & arr[b]);
}
return i & 0x3f;
或者直观一点 , 把需要位移的位数弄成变量 , 更直观 , 当每组左右部分都是16bit的时候 , 这轮运算完毕就可以 & 0x3f 返回结果了
int[] arr = new int[]{0x55555555, 0x33333333, 0x0f0f0f0f, 0x00ff00ff, 0x0000ffff};
for (int a = 1, b = 0; a <= 16; a <<= 1, b++) {
i = (i & arr[b]) + ((i >>> a) & arr[b]);
}
return i & 0x3f;
简单举例
例如到了第二步了 , 10001110
mask是 0x33333333 , 也就是 001100110011......
每组4bit,要合并左右两个bit的bit位数 , (i & mask) + (i >>> n) & mask 格式 .
i&mask , 先取出右侧的位数 也就是 0000 0010 ,
用 (i >>> n) & mask 取出左侧位数 也就是先右移两位在 & mask , 就是 0010 0011
相加之后 就是 0010 0101 .
例如右边那组 , 原来是 11 10 , 也就是左侧3个1 , 右侧2个1 ,合并之后 , 总共 101 , 也就是5个1 .
官方的优化
会发现,源码里面除了第二行,全都不是标准的 (i & mask) + (i >>> n) & mask
所以除了每组4bit,也就是第二行的操作 , 其他的都是在优化
public static int bitCount(int i){
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
第一行的优化 , 也就是每组2bit,第一次运算的时候 , 稍稍特殊一些
首先忽略奇数位(当奇数位都是0) , 如果单纯只想算偶数位的1的个数 .
直接 (i & 0xaaaaaaaa) - ((i >>> 1) & 0x55555555); 就行 (a就是1010)
其实只有两种情况 ,
- 例如10 , 右侧是01 . 减一减就是偶数位bit1个数 , 也就是1 .
- 00 - 00 = 0 , bit位个数也是0 , 正确.
奇数部分本身就是低位 , 做 i & 0x55555555 的操作 ,这俩加一加,可以得出每两位的bit1位数
(i & 0xaaaaaaaa) - ((i >>> 1) & 0x55555555) + i & 0x55555555.
(i & 0xaaaaaaaa) 和 i & 0x55555555 合并一下 . 那就是i本身了 , 所以最终是 i - ((i >>> 1) & 0x55555555);
这样是比 (i & mask) + (i >>> n) & mask 的这种运算减少一点压力的
第三行的优化,每组8bit的优化
第三行是每组8bit . 因为在4bit那组 . 确定了每组最多是4个 , 也就是100个
那么最大是 0100 + 0100 = 1000 . 是超不过4个bit的 .
所以 第三行的 原本应该是(i & 0x0f0f0f0f) + ((i >>> 4) & 0x0f0f0f0f)
实际上可以先加再&mask 也就是 (i + (i >>> 4)) & 0x0f0f0f0f
每组16和32bit的优化
到了底下的每组8 或者16bit(也就是合并左右8bit的bit1位数) 这里完全不用&mask了 .
因为 最后两行相加左右两部分的bit位数 , 最多也就是16和32个,32也才100000 , 最多也只占5和6个bit .
高位不削除 , 让他留着也无所谓 , 最后只取末尾6bit就好.
(因为第四步没削高位 , 第9位到第14位其实也有值 ,也就是右往左数第二组8bit的后6位 ,但是不管他就好)
最后 & 0x3f (其实 &0xff也是一样的,只不过右往左数第7,8位必定是0) , 取出最后的6个bit位 , 就是最后的结果:bit1位数