在这里对源码中Integer的部分位运算方法做一个分析解读。同理,可以比较一下其他Number的子类,对位运算部分的设计和性能有一个理解。

在java中,Integer是int类型的包装类型,占4个字节,共32位。我们按顺序分析如下几个重要的静态方法:
public static int highestOneBit(int i)

功能:返回输入值不为0的最高位保留,其他设置为0的数
举例:输入10(00001010),输出8(00001000)
分析:

(1)i |= (i >> 1);
算数右移1位,左补1位符号位;与原数或运算,赋值给原数,保证为1的最高位和接着的1位都为1。
(2)i |= (i >> 2);
算数右移2位,左补2位符号位;与原数或运算,赋值给原数,保证为1的最高两位和接着的两位都为1。
(3)i |= (i >> 4);
算数右移4位,左补4位符号位;与原数或运算,赋值给原数,保证为1的最高四位和接着的四位都为1。
(4)i |= (i >> 8);
算数右移8位,左补8位符号位;与原数或运算,赋值给原数,保证为1的最高八位和接着的八位都为1。
(5)i |= (i >> 16);
算数右移16位,左补16位符号位;与原数或运算,赋值给原数,保证为1的最高十六位和接着的十六位都为1。(可以想象一下8位二进制10000000,如何才能把8位全部变为1?)
(6)i - (i >>> 1);
逻辑右移1位,左补0,结果就是不为0的最高位变为0,后面全都是1;再用原数减去移位后的结果,即为最终结果。
public static int numberOfLeadingZeros(int i)

功能:统计前导0的个数
举例:输入0x00000080,输出24
分析:

(1)if (i == 0)
return 32;
当输入为0时,前导0的个数是32位,为提高效率,不需要执行后续逻辑直接返回。
(2)int n = 1;
n为待返回前导0的个数,此处赋初始值为1,后续有用。
(3)if (i >>> 16 == 0) { n += 16; i <<= 16; }
逻辑右移16位,左补16位0,也就是在判断高16位的结果:
如果是0,说明高16位全部都是0位,因此结果至少有16个0,将n累加16。接着左移16位,将待判断的32位的低16位变为高16位;
如果不为0,说明高16位有不为0位。
(4)if (i >>> 24 == 0) { n += 8; i <<= 8; }
逻辑右移24位,左补24位0,也就是在判断高8位的结果:
如果是0,说明高8位全部都是0位,因此结果至少有8个0,将n累加8。接着左移8位,将待判断的16位的低8位变为高8位;
如果不为0,说明高8位有不为0位。
(5)if (i >>> 28 == 0) { n += 4; i <<= 4; }
逻辑右移28位,左补28位0,也就是在判断高4位的结果:
如果是0,说明高4位全部都是0位,因此结果至少有4个0,将n累加4。接着左移4位,将待判断的8位的低4位变为高4位;
如果不为0,说明高4位有不为0位。
(6)if (i >>> 30 == 0) { n += 2; i <<= 2; }
逻辑右移30位,左补30位0,也就是在判断高2位的结果:
如果是0,说明高2位全部都是0位,因此结果至少有2个0,将n累加2。接着左移2位,将待判断的4位的低2位变为高2位;
如果不为0,说明高2位有不为0位。
(7)n -= i >>> 31;
逻辑右移31位,左补31位0,也就是在判断高1位的结果:
分别有0、1两种情况。当为1时,减去1,正好将在第(2)步多加的1清0;当为0时,减去0,正好将在第(2)步多加的1匹配正确值。
为什么不判断低1位?
假设要判断的2位在最左端:00(第(6)判断)、01(第(7)判断)、10(第(7)判断)、11(第(7)判断)
假设要判断的2位在最右端:00(第(1)判断)、01(第(7)判断)、10(第(7)判断)、11(第(7)判断)
覆盖所有情况。
public static int bitCount(int i)

功能:统计值为1的个数
举例:输入0101 0101 0101 0101 0101 0101 0101 0101,输出16
分析:


(1)i = i - ((i >>> 1) & 0x55555555);
以2位为最小组,分析4种情况。
00=0(1的个数)=>00(i)-00(i逻辑右移1位)=00(结果就是1的个数)=0
01=1=>01-00=01=1
10=1=>10-01=01=1
11=2=>11-01=10=2
根据以上推断,((i >>> 1) & 0x55555555)就是为了得到以2位为最小组的值,由于两位中的高1位受上一位的影响,但又需要必须是0,所以与0x55555555操作,将2位小组的高一位抹为0来符合上述逻辑。
每2位代表1的个数。
(2)i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
将2位1组相加合并结果至4位一组。
i & 0x33333333 低两位。
(i >>> 2) & 0x33333333高两位,但高阶的两位被前两位影响,因此高2位抹为0
每4位代表1的个数。
(3)i = (i + (i >>> 4)) & 0x0f0f0f0f;
将4位1组相加合并结果至8位一组。
i 低四位
i >>> 4 高四位,因为8位最多8个1,因此可以直接使用低4位表示。
结果& 0x0f0f0f0f,高4位无用,用0抹去。
(4)i = i + (i >>> 8);
将8位1组相加合并结果至16位一组。
i 低8位
i >>> 8 高8位,因为16位最多16个1,因此可以直接使用低8位表示。低8位有效值,高8位无意义。
(5)i = i + (i >>> 16);
将16位1组相加合并结果至32位一组。
i 低16位
i >>> 16 高16位,因为32位最多32个1,因此可以直接使用低8位表示。
(6)i & 0x3f;
因为32位最多32个1,因此可以直接使用低6位表示即可,高位用0抹去。
public static int reverse(int i)

功能:按位反转
举例:输入0x00000080,输出0x01000000
分析:

(1)i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
(i & 0x55555555) << 12位1组,低位变高位,低位为0
(i >>> 1) & 0x555555552位1组,高位变低位,高位为0
或的结果就是2位1组,高低位互换
(2)i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
(i & 0x33333333) << 24位1组,低2位变高2位,低位为0
(i >>> 2) & 0x333333334位1组,高2位变低2位,高位为0
或的结果就是4位1组,高2与低2位互换
(3)i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
同理,8位1组,高4与低4位互换
(4)i = (i << 24) | ((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) | (i >>> 24);
以8位为1组,因为组数已经比较少了,直接逆排序很简单