Integer中bitCount方法的源码解析

Integer 中的 bitCount() 方法用于统计二进制数字中 bit 为 1 的个数,例如 0b111 中 bit 为 1 的个数有 3 位。

源码为:

public static int bitCount(int i) {  
    // HD, Figure 5-2  
    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;  
}

对于统计二进制数字中 bit 为 1 的个数,我们首先可能会想到使用遍历二进制数字上的所有位数来求和。最多需要循环 32 次。

事实上,统计二进制数字中 bit 为 1 的个数,我们一般使用汉明码来计算,过程如下:

  1. 先统计每两位 bit 中 1 的个数,方法是 (i & 0x55555555) + ((i >>> 1) & 0x55555555)0x55555555 对应的二进制数字为 01010101010101010101010101010101(i & 0x55555555) 能得到相邻两位 bit 中低位中 1 的个数,((i >>> 1) & 0x55555555) 能得到相邻两位 bit 中高位中 1 的个数,相加便为每两位 bit 中 1 的个数。
  2. 再统计每 4 位 bit 中 1 的个数。
  3. 再统计每 8 位 bit 中 1 的个数。
  4. 再统计每 16 位 bit 中 1 的个数,得到最后结果。

这是一种分而治之的算法,代码如下:

i = (i & 0x55555555) + ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i & 0x0f0f0f0f) + ((i >>> 4) & 0x0f0f0f0f);
i = (i & 0x00ff00ff) + ((i >>> 8) & 0x00ff00ff);
i = (i & 0x0000ffff) + ((i >>> 16) & 0x0000ffff);
return i & 0b111111;

对应的二进制代码为:

i = (i & 01010101010101010101010101010101) + ((i >>> 1) & 01010101010101010101010101010101);  
i = (i & 00110011001100110011001100110011) + ((i >>> 2) & 00110011001100110011001100110011);
i = (i & 00001111000011110000111100001111) + ((i >>> 4) & 00001111000011110000111100001111);
i = (i & 00000000111111110000000011111111) + ((i >>> 8) & 00000000111111110000000011111111); 
i = (i & 0b00000000000000001111111111111111) + ((i >>> 16) & 0b00000000000000001111111111111111);
return i & 0b111111;

但是这代码明显跟源码不一样,因为源码做了优化。

在第一行代码中,每两位 bit 位共有 3 种情况。

  1. (0b10 & 0x55555555) + ((0b10 >>> 1) & 0x55555555) = 0b00 + 0b01 = 0b01 ,同时 0b10-0b01=0b01 也成立
  2. (0b01 & 0x55555555) + ((0b01 >>> 1) & 0x55555555) = 0b01 + 0b00 = 0b01 ,同时 0b01-0b00=0b01 也成立
  3. (0b11 & 0x55555555) + ((0b11 >>> 1) & 0x55555555) = 0b01 + 0b01 = 0b10 ,同时 0b11-0b01=0b10 也成立

能看出来,(i & 0x55555555) + ((i >>> 1) & 0x55555555) 等同于 i - ((i >>> 1) & 0x55555555),而使用 i - ((i >>> 1) & 0x55555555) 能减少一次移位操作。

再看第三行代码中,因为 8 位 bit 最多存在 8 个 1,对应的二进制数字为 0b1000 ,可以使用 4 位 bit 位存起来,所以 i 可以直接和 ((i >>> 2) & 0x33333333) 相加,将结果存储在原来的 4 位 bit 位上。

再看第二行代码,因为 4 位 bit 位最多存在 4 个 1,对应的二进制数字为 0b0100,超出了 2 位 bit 可以存的位数,所以需要保持原来的移位。

第四,第五行也同理,因为 int 只有 32 位,对应二进制数字为 0b100000,可以使用 5 位 bit 存起来,未超出 8 位,所以直接移位后相加。

最后第六行,只保留低 5 位 bit 作为结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值