常见位运算技巧总结:从基础到高阶应用
一、基础位运算
1.基础位运算速查表
运算符 | 名称 | 行为描述 | 示例(以a=60(00111100) , b=13(00001101) 为例) |
---|---|---|---|
& | 按位与 | 同1得1,其余得0 | a & b → 12 (00001100) |
| | 按位或 | 有1得1 | a | b → 61 (00111101) |
^ | 按位异或 | 不同得1,相同得0 | a ^ b → 49 (00110001) |
~ | 按位取反 | 0变1,1变0(含符号位) | ~a → -61 (补码表示) |
<< | 左移 | 低位补0,高位溢出丢弃 | a << 2 → 240 (11110000) |
>> | 右移 | 高位补符号位,低位溢出丢弃 | -8 >> 1 → -4 (带符号右移) |
2.位运算优先级
完整优先级列表(从高到低):
运算符 | 描述 | 示例 | 注意事项 |
---|---|---|---|
~ | 按位取反 | ~a | 单目运算符,最高优先级 |
<< >> >>> | 位移运算 | a<<2 | 优先级低于算术运算符 |
& | 按位与 | a & b | 高于比较运算符 |
^ | 按位异或 | a ^ b | 优先级高于逻辑或 |
` | ` | 按位或 | `a |
常见错误示例:
// 错误:实际等价于 a & (1 == 0)
if (a & 1 == 0) { ... }
// 正确写法
if ((a & 1) == 0) { ... }
3.异或运算运算律
数学性质:
- 交换律:
a ^ b = b ^ a
- 结合律:
(a ^ b) ^ c = a ^ (b ^ c)
- 自反性:
a ^ a = 0
- 恒等律:
a ^ 0 = a
- 可逆性:若
a ^ b = c
,则c ^ b = a
重要限制:
- 不满足分配律:
a ^ (b & c) ≠ (a ^ b) & (a ^ c)
- 不能直接用于非整型数据(浮点数需特殊处理)
二、高频操作技巧
1.判断二进制第x位是0还是1
public class BitCheck {
// 方法1:使用位掩码(推荐,正确处理负数)
public static boolean isBitSet(int n, int x) {
return (n & (1 << x)) != 0;
}
// 方法2:右移后与1比较(注意符号位影响)
public static boolean isBitSetByShift(int n, int x) {
return ((n >> x) & 1) == 1;
}
public static void main(String[] args) {
int num = 0b1001; // 二进制1001(十进制9)
System.out.println(isBitSet(num, 3)); // true(第3位是1)
System.out.println(isBitSet(num, 2)); // false(第2位是0)
}
}
关键点:
- 位掩码原理:
1 << x
生成形如000...100...
的掩码,与n
进行&
操作后,仅保留目标位 - 负数处理:使用方法1可正确处理负数,右移方法在负数高位会补1
- 参数校验:建议添加
if (x < 0 || x > 31) throw...
2.将第x位修改为1
public class BitSetter {
public static int setBit(int n, int x) {
return n | (1 << x);
}
public static void main(String[] args) {
int num = 0b1001; // 原值9(1001)
int result = setBit(num, 2);
System.out.println(Integer.toBinaryString(result)); // 1101(13)
}
}
核心逻辑:
- 或操作特性:
0 | 1 = 1
,1 | 1 = 1
,保证目标位被置1 - 示例流程:
原始值:1001 掩码: 0100 (1 << 2) 结果: 1101 (1001 | 0100)
- 数据类型扩展:处理long时需用
1L << x
3:将第x位修改为0
public class BitClear {
public static int clearBit(int n, int x) {
return n & ~(1 << x);
}
public static void main(String[] args) {
int num = 0b1001; // 原值9(1001)
int result = clearBit(num, 3);
System.out.println(Integer.toBinaryString(result)); // 0001(1)
}
}
实现原理:
- 取反掩码:
~(1 << x)
生成形如111...011...
的掩码 - 与操作特性:
0 & 0 = 0
,1 & 0 = 0
,强制清除目标位 - 操作示例:
原始值:1001 取反掩码:0111 (~1000) 结果: 0001 (1001 & 0111)
4.提取最右侧的1
public class RightmostOne {
// 获取最右侧1的数值(例如12=1100返回4=100)
public static int getRightmostOne(int n) {
return n & -n;
}
// 获取最右侧1的位置索引(例如12=1100返回2)
public static int getRightmostOnePosition(int n) {
return Integer.numberOfTrailingZeros(n & -n);
}
public static void main(String[] args) {
System.out.println(getRightmostOne(12)); // 4 (二进制100)
System.out.println(getRightmostOne(7)); // 1 (二进制001)
System.out.println(getRightmostOnePosition(12)); // 2(第2位)
}
}
实现原理:
- 二进制补码特性:
-n
是n
的二进制补码(取反+1) - 位与操作:
n & -n
会保留最右侧的1,其他位清零示例:n=12 (1100) -n = 补码 0100 1100 & 0100 = 0100 (十进制4)
5.清除最右侧的1
public class ClearRightmostOne {
public static int clearRightmostOne(int n) {
return n & (n - 1);
}
public static void main(String[] args) {
System.out.println(clearRightmostOne(12)); // 8 (1000 -> 清除1100的最右侧1)
System.out.println(clearRightmostOne(7)); // 6 (清除0111的最右侧1得0110)
System.out.println(clearRightmostOne(1)); // 0
}
}
核心逻辑:
- 减1操作:
n-1
会将最右侧的1变为0,右侧所有0变为1 - 与操作:
n & (n-1)
清除变化的位示例:n=12 (1100) n-1=11 (1011) 1100 & 1011 = 1000 (十进制8)
三.位图思想(Bitmap)
核心概念:使用二进制位(bit)作为存储单元,通过位的位置(offset)映射数据索引,每位0/1表示存在与否。
核心优势:
- 空间效率:1个int(32位)可存储32个状态,空间压缩率高达32倍
- 操作高效:位运算指令直接在寄存器执行,时间复杂度O(1)
典型应用场景:
- 海量数据去重(布隆过滤器)
- 权限控制系统(每位代表一个权限)
- 图像二值化处理
public class Bitmap {
private int[] bits;
// 初始化位图(存储maxSize个元素)
public Bitmap(int maxSize) {
bits = new int[(maxSize >> 5) + 1]; // 除以32并向上取整
}
// 设置某位为1
public void set(int pos) {
int arrIdx = pos >> 5; // 确定在哪个int
int bitIdx = pos & 0x1F; // 取低5位确定具体bit
bits[arrIdx] |= (1 << bitIdx);
}
// 检查某位是否为1
public boolean get(int pos) {
int arrIdx = pos >> 5;
int bitIdx = pos & 0x1F;
return (bits[arrIdx] & (1 << bitIdx)) != 0;
}
}