java中的mod运算_Java源码中Integer类位运算分析

本文详细分析了Java Integer类中的几个重要位运算方法:highestOneBit用于找到最高位的1,numberOfLeadingZeros统计前导0的个数,bitCount计算值为1的位数,reverse实现按位反转。通过源码解析,解释了这些方法背后的逻辑和优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

42376bc92c36d09a71acb07b868ae11e.png

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

public static int highestOneBit(int i)

6480199e789ebe795ddf5e2154f354dc.png

功能:返回输入值不为0的最高位保留,其他设置为0的数

举例:输入10(00001010),输出8(00001000)

分析:

c829961659eff75e8dfb8dffb30a80d0.png

(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)

f30876309fbb450960c8aced0ee9a6e4.png

功能:统计前导0的个数

举例:输入0x00000080,输出24

分析:

fc7651a1b37fb441be5c0433513bacbf.png

(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)

625b93bd0b2790335a3f6fc01525b809.png

功能:统计值为1的个数

举例:输入0101 0101 0101 0101 0101 0101 0101 0101,输出16

分析:

4cc6a9d4627f363931848527e373c604.png
8cf4a16b5f01f0bfaa74f98751f5eb3f.png

(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)

63c5fa73ba469e8d05ef911fb3262ffe.png

功能:按位反转

举例:输入0x00000080,输出0x01000000

分析:

9324ded18345cde4a413478c7b5310c5.png

(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组,因为组数已经比较少了,直接逆排序很简单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值