移位运算符是啥?其实就是把一个数的二进制形式往左或者往右挪挪位置,说白了就是“搬家”。移位运算符有三个:左移(
<<
)、带符号右移(
>>
)、无符号右移(
>>>
)。搞懂它们的使用场景和区别,你就能轻松驾驭。
首先,左移(
<<
)就是简单粗暴的向左移动若干位,高位丢弃,低位补零。比如:
int x = 2; // 二进制是 00000010
int result = x << 2; // 左移两位,变成 00001000
System.out.println(result); // 输出 8,相当于 2 * 2^2
没错,左移实际上相当于乘以 2 的 n 次方(注意溢出问题)。
接着是带符号右移(
>>
)。它会根据符号位(最高位)来填充空出的高位。如果是正数,补零;如果是负数,补 1。看个例子:
int y = -8; // 二进制是 11111111111111111111111111111000
int result = y >> 2; // 右移两位,变成 11111111111111111111111111111110
System.out.println(result); // 输出 -2,相当于 -8 / 2^2
这种操作可以用来做快速的除法运算,特别是在对负数处理上,它能保留符号位,结果依然正确。
再来看无符号右移(
>>>
)。这个运算符不管符号位,全都补零。它的应用场景更特殊,多用在处理二进制数据或某些位操作的需求中:
int z = -8; // 二进制是 11111111111111111111111111111000
int result = z >>> 2; // 无符号右移两位,变成 00111111111111111111111111111110
System.out.println(result); // 输出 1073741822,负数变成了一个大正数
注意,
>>>
对于负数来说会发生这种“神奇”的变化,因为它直接把符号位当普通位处理。
移位操作的性能优势
说到移位操作的原因,核心就是高效!移位操作直接对应处理器的移位指令,通常一个时钟周期就能完成,远比乘法或除法快。此外,移位还能帮助我们节省内存,比如在使用一个整数存储多个布尔值时,移位操作就是你的好帮手。
来看个实际例子,在
HashMap
的
hash
方法中,移位运算符就被用得明明白白:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里用到了按位异或(
^
)和无符号右移(
>>>
)。通过无符号右移 16 位,把高位和低位的 hashCode 混合到一起,提高了散列的均匀性。简单高效,还能避免 hash 冲突。
移位位数超过类型位宽怎么办?
有意思的是,如果移位的位数超过了变量类型的位宽,比如
int
是 32 位,
long
是 64 位,Java 会自动对移位数进行取模操作。例如:
int a = 1; // 二进制是 00000001
System.out.println(a << 35); // 实际上等同于 a << 3,输出 8
因为
35 % 32 = 3
,所以移位 35 位等价于移位 3 位。对于
long
类型,同理会取模 64。
移位操作的限制
要注意,移位操作只支持整数类型(
int
和
long
),不支持
float
和
double
。这是因为浮点数在二进制中的表示方式比较特殊,移位操作会让它变得复杂且难以预测。
还有一点,
byte
、
short
、
char
在移位时会先被转成
int
类型再操作,所以最终结果也是
int
类型。