核心概念
Java 提供三种移位运算符,用于对整型(int、long)的二进制位进行移动操作:
| 运算符 | 名称 | 行为 | 符号位 |
|---|---|---|---|
<< | 左移 | 二进制位左移,低位补 0 | 可能改变 |
>> | 有符号右移 | 二进制位右移,高位补符号位值 | 保留 |
>>> | 无符号右移 | 二进制位右移,高位始终补 0 | 不保留(变正) |
移位运算符的操作数只能是整型(
byte、short、int、long、char)。对byte/short/char操作时,会先提升为int。
左移 <<
规则
value << n
- 将
value的二进制位整体左移 n 位 - 左侧溢出的高位丢弃
- 右侧低位补 0
- 等效于:乘以 2ⁿ(在不溢出的前提下)
示例
int a = 5; // 二进制:0000 0000 0000 0000 0000 0000 0000 0101
int b = a << 2; // 结果:20
// 二进制:0000 0000 0000 0000 0000 0000 0001 0100
int a = -5; // 二进制:1111 1111 1111 1111 1111 1111 1111 1011(补码)
int b = a << 2; // 结果:-20
有符号右移 >>
规则
value >> n
- 将
value的二进制位整体右移 n 位 - 右侧溢出的低位丢弃
- 左侧高位补符号位的值(正数补 0,负数补 1)
- 等效于:除以 2ⁿ 后向下取整(向负无穷方向)
示例
正数情况:高位补 0
int a = 20; // 二进制:0000 0000 0000 0000 0000 0000 0001 0100
int b = a >> 2; // 结果:5
// 二进制:0000 0000 0000 0000 0000 0000 0000 0101
负数情况:高位补 1
int a = -20; // 二进制:1111 1111 1111 1111 1111 1111 1110 1100
int b = a >> 2; // 结果:-5
// 二进制:1111 1111 1111 1111 1111 1111 1111 1011
>>保留符号位——负数右移后仍然是负数。
>> 与除法 ÷ 的区别
20 >> 2 = 5 20 / 4 = 5 ✅
-20 >> 2 = -5 -20 / 4 = -5 ✅(可整除时一致)
-1 >> 1 = -1 -1 / 2 = 0 ❌(不可整除时不同)
-3 >> 1 = -2 -3 / 2 = -1 ❌
原因:>> 是向下取整(floor),而 Java 的整数除法是向零取整(truncation toward zero)。
无符号右移 >>>
规则
value >>> n
- 将
value的二进制位整体右移 n 位 - 右侧溢出的低位丢弃
- 左侧高位始终补 0(无论正负)
- 不保留符号位,负数右移后变成正数
示例
int a = -20;
// 二进制:1111 1111 1111 1111 1111 1111 1110 1100
int b = a >>> 2;
// 结果:1073741819
// 二进制:0011 1111 1111 1111 1111 1111 1111 1011
常见用途
- 处理无符号语义的数值,如
int作为位图使用 HashMap中计算哈希扰动:h = key.hashCode() ^ (h >>> 16)Arrays.binarySearch中的中点计算:(low + high) >>> 1(防止溢出)
(low + high) >>> 1相比(low + high) / 2的优势当
low + high超过int最大值(2³¹ - 1)时,会溢出成负数,此时/2得到错误结果,而>>> 1把溢出的结果当作无符号数处理,得到正确的中间位置。JDK 源码中广泛使用此写法。
移位距离的取模规则
规则
Java 规范规定:移位距离 n 会对操作数的位数取模后再执行移位。
| 操作数类型 | 实际移位距离 | 取值范围 |
|---|---|---|
int(32 位) | n & 0x1F(即 n % 32) | 0 ~ 31 |
long(64 位) | n & 0x3F(即 n % 64) | 0 ~ 63 |
示例
int a = 1;
a << 32; // 等效于 1 << 0 = 1(不移动)
a << 33; // 等效于 1 << 1 = 2
a >> 32; // 等效于 1 >> 0 = 1踩坑提醒
如果写
1 << 32,你以为会得到 0(所有位移出),实际上因为取模规则,等于1 << 0,结果仍然是 1。
三大运算符对比
| 操作 | 5 << 2 | 20 >> 2 | -20 >> 2 | -20 >>> 2 |
|---|---|---|---|---|
| 结果 | 20 | 5 | -5 | 1073741819 |
| 符号位 | 可能变 | 保留 | 保留 | 变正 |
| 高位补 | 0 | 符号位 | 符号位 | 0 |
| 数学含义 | × 2ⁿ | ÷ 2ⁿ(向下取整) | ÷ 2ⁿ(向下取整) | 无符号右移 |
常见应用场景
1. ArrayList 扩容 —— >>
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容 1.5 倍oldCapacity >> 1 = oldCapacity / 2,相加得到 oldCapacity * 1.5。用移位代替除法,性能更高。
2. HashMap 哈希扰动 —— >>>
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}将高 16 位与低 16 位异或,让高位信息参与低位运算,降低哈希碰撞概率。
3. 二分查找中点计算 —— >>>
int mid = (low + high) >>> 1; // 防溢出4. 位图 / 权限掩码 —— <<
int READ = 1 << 0; // 0001 = 1
int WRITE = 1 << 1; // 0010 = 2
int EXECUTE = 1 << 2; // 0100 = 4
int permission = READ | WRITE; // 0011,拥有读写权限
boolean canWrite = (permission & WRITE) != 0; // true5. 颜色通道提取 —— >>> 与 &
int pixel = 0xAABBCCDD;
int alpha = (pixel >>> 24) & 0xFF; // 0xAA
int red = (pixel >>> 16) & 0xFF; // 0xBB
int green = (pixel >>> 8) & 0xFF; // 0xCC
int blue = (pixel) & 0xFF; // 0xDD性能说明
移位运算直接在 CPU 寄存器级别完成,是最快的整数运算之一:
| 运算 | 周期数(约) |
|---|---|
a * 2 | 3~5 周期(乘法) |
a << 1 | 1 周期(移位) |
现代 JIT 编译器会自动优化
n * 2为n << 1、n / 2为n >> 1,但显式使用移位运算符可以提高代码的可读性,表明”我是在做位运算,不是算术”。
面试要点
>>和>>>的区别:>>保留符号位(正补0负补1),>>>强制补 0- 移位替代乘除法:
<< 1等效 ×2,>> 1等效 ÷2(向下取整),性能更优 - 取模陷阱:
int移位距离对 32 取模,1 << 32 = 1而不是 0 >>> 1防溢出:二分查找中(low + high) >>> 1防止整数溢出- ArrayList 扩容:
old + (old >> 1)= ×1.5 倍扩容
参考链接
- 扩容机制详解(ArrayList) — ArrayList 扩容中用到的
>> - Java集合框架 — 集合框架 MOC