发布于2026-07-24 阅读(0)
扫一扫,手机访问
位运算这块,很多人学的时候觉得概念太多,原码、反码、补码、左移右移,混在一起容易晕。但真正搞懂了,会发现它其实非常规整,而且很多底层框架都在用。我们先从几个容易让人困惑的点说起。
先问一个看似简单,但很多人不一定答得上来问题:为什么只有无符号右移(>>>),而没有无符号左移(<<<)?
答案其实很直接:左移操作本身就和符号位无关。在计算机里,位运算中的左移,最高位就是符号位,左移时,最高位直接就被丢弃了,低位补0。所以,不管符号位是什么,左移后它都会消失,不存在“保留符号位”或“不保留符号位”的选项。

举个例子,8位的byte类型,最高位如果是1,那它就表示负数。把它左移一位,结果就是:最高位的1被丢掉,最低位补0。比如 1111 1111 << 1 的结果是 1111 1110。所以,不只是Ja va,任何语言都没有“无符号左移”这个东西,因为技术上没有实现的必要。
这是一个很基础,但也很容易搞混的概念。原码、反码、补码,它们是同一种数字的不同“编码方式”。
0000 0001,-1原码是 1000 0001。0000 0001,-1的反码是 1111 1110。0000 0001,-1的补码是 1111 1111。关键点:计算机内部,所有数字都是以补码的形式存储的。 这样做的好处是,可以把减法统一成加法来运算,简化电路设计,而且还能多表示一个最低数。比如8位二进制,原码和反码能表示的范围是[-127, +127],而补码能表示[-128, +127]。
左移的规则很简单:把二进制位整体向左移动,左边超出的部分直接丢弃,右边空出来的位置补0。这个过程不考虑符号位,因为符号位也会被移动和丢弃。
正数
以数字10为例,它的二进制补码是:
00000000 00000000 00000000 00001010
左移1位后,变成:
00000000 00000000 00000000 00010100
结果就是十进制20,相当于乘以2。HashMap在扩容时,将新容量设为旧容量的2倍,用的就是左移。
负数
以负数-10为例,它的二进制补码是:
1111 1111 1111 1111 1111 1111 1111 0110
左移1位后,变成:
1111 1111 1111 1111 1111 1111 1110 1100
结果是-20,同样是乘以2的效果。
右移和左移不同,它需要考虑符号位。规则是:把二进制位整体向右移动,左边空出来的位置,用原来的符号位来填充(正数补0,负数补1),右边超出的部分直接丢弃。
正数
以10为例:
00000000 00000000 00000000 00001010
右移1位后,变成:
00000000 00000000 00000000 00000101
结果是5,相当于除以2。ArrayList扩容时,将新容量增加为原来的1.5倍,用的就是右移。
负数
以负数-10为例:
1111 1111 1111 1111 1111 1111 1111 0110
右移1位后,左边补1:
1111 1111 1111 1111 1111 1111 1111 1011
结果是-5,也是除以2的效果。
无符号右移,顾名思义,就是不关心符号位,不管正数负数,左边空出来的位一律补0,右边超出的部分直接丢弃。
正数
结果和普通的右移完全一样,因为正数符号位就是0。
负数
以-10为例:
1111 1111 1111 1111 1111 1111 1111 0110
无符号右移1位后,左边补0:
01111 1111 1111 1111 1111 1111 1111 1011
结果变成了一个非常大的正数:2147483643。HashMap在计算一个数最接近的2的N次幂时,就大量使用了无符号右移。
如果对一个int类型的数字左移超过32位,比如左移33位,实际效果等同于左移1位。右移和无符号右移同理。
a << n == a << (n % 32)
说白了,就是对位移的位数做了一个取模运算。这是一个很实用的底层细节。
位运算看似复杂,但核心逻辑非常清晰。左移和右移,一个不管符号位,一个管符号位;无符号右移,则是对右移的一个补充,专门为了处理负数而设计。理解到位运算的底层逻辑,写代码时会更有底气,尤其是在阅读像HashMap、ArrayList这种核心源码时,会感觉豁然开朗。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8