您的位置:首页 > 编程语言 > Java开发

JDK源码学习--JDK中Integer类的BitCount方法实现过程

2017-10-19 16:59 204 查看
以下是该方法的实现:



首先说说此方法的作用,此方法就是为了计算参数i对应的二进制表示形式中二进制位‘1’的数量。初次提出这个需求我们肯定会想到可以通过遍历这个数对应的字符串表示形式,然后设置一个变量用来记录比特位1的数量。这种方法虽然可以,但不免麻烦,所以jdk提供了另外一种计算方式,该方式的目的就是通过对原二进制串的一系列转化,使得最终二进制对应的数字的大小就是原二进制串中比特1的数量。

此方法的设计思路是:先将二进制数分成两个一组,然后将这些双比特位的数转化为某种形式,通过这种形式可以直接计算出二进制位的个数,然后将这些特殊形式的数两两相加,(得到下图第三行),然后再将得到的数相加,直到最终得出一个单数为止,这个单数的十进制表示形式就是原本二进制串中比特位1的个数。



可以先将这些双比特位数及其对应的这种形式枚举出来,如下图;



对这个图的理解是:

例如二进制数11,有两个1,所以将其转化为10,二进制数10(2)的十进制表示形式是2,正好是比特位1的个数

现在对于这个图可以抽象出一个数学问题,即已知src列,求store列?

设store = src – x

可以将上图中的数据代入这个公式:

00 = 00 – 00

01 = 01 – 00

01 = 10 – 01

10 = 11 – 10

很容易看出来x其实是src移位之后的值,即

x = src >>> 1

所以原公式为

store = src – ( src >>> 1)

以上只适用于双比特位的二进制数,但是一个int类型的数占四个字节,即32个比特位。

那么直接这样进行移位运算坑定是有问题的。假设把一个32位的二进制数两两分组,这样确实可以计算出每个双比特的数中比特位1的数量,但是在进行移位运算时,左边数必然会溢出到右边,这样就会影响右边数的计算。所以还要消除左边数溢出带来的影响。

即: store = src – ( src >>> 1) & 01(此处有16个01这样的序列)

其中 0101 0101 0101 0101 0101 0101 0 的16进制表示形式是 :0x5555 5555

这便是 程序中第一行的来历



然后再来看看怎样计算四个二进制位的数中比特1的个数



按照前面所说,四个二进制位其实是由前面的双比特位数两两相加得到的,从这个图中也可以看出(比如图中01和10合并为0011)

现在假设这个四位的比特数是aabb,其中双比特位aa和bb都不是原数,而是经过转化的‘特殊形式’,我们要计算这个四位比特数中比特位1的个数,只需要将aa和bb相加就行了

其中低两位bb可以通过与运算 bb = aabb & 0011来获得,而高两位aa的计算稍显复杂,首先要通过移位运算移除低两位,即变成00aa(bb),bb溢出到其右边,需要与0011进行与运算消除溢出带来的影响

所以对于四位的比特数有以下公式:

λ =( i & 0x0011) + (i>>>2 & 0x0011)

一个int类型有8个这样的比特数,所以

λ =( i & 0x33333333) + (i>>>2 & 0x33333333)

同理求8位里面的两边4位之和:

λ =( i + i>>>4) & 0x0F0F0F0F

i >>> 4表示往右移动了4位,然后在与i相加,相当于每8位一组,然后8位中的高4位与低4位相加储存在低4位中,然后这里在与0x0f0f0f0f进行与运算,把高4位完全置为0了,因为0x0f0f0f0f用二进制表示就是00001111000011110000111100001111,看到这里可能有些困惑,这里为什么要与0x0f0f0f0f进行与运算,因为每8位一组的话,最多也就是8,那么4位数足够了,高4位就没有必要了。

对于16位的比特数:

i = i + (i >>> 8);

其高八位和低八位相加,子所以没有进行与运算是因为无论是高八位还是低八位,它们的前四位在前面的运算中已经置为0,不会产生进位影响前面的数。

以上就是bitCount方法的实现原理

参考自:

http://blog.csdn.net/cor_twi/article/details/53720640

http://type.so/java/java-lang-integer.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐