有时候我们需要检查数值中的某个二进制位是否被置位。这种情况通常发生在将数值用作标志集合时,其中每一位代表特定的布尔值。本文将探讨从 byte、short、char、int 和 long 等整型值中提取特定位的多种方法,包含完整示例代码。
测试特定位
最常见的需求是使用位掩码测试整数值中的特定位。例如检查 byte 值的第三位是否置位:
byte val1 = 0b0110_0100;
byte mask = 0b0000_0100;
boolean isSet1 = (val1 & mask) > 0;
assertTrue(isSet1);
这里通过按位与操作测试二进制数 01100100
的第三位(00000100
)。结果大于零表示该位已置位。同样可以测试未置位的情况:
byte val2 = 0b0110_0010;
boolean isSet2 = (val2 & mask) > 0;
assertFalse(isSet2);
此方案基于 byte 类型,但可轻松扩展到 short、char、int 和 long。不过上述方法硬编码了掩码,若需检查任意位置就需要更通用的方案。
使用移位运算符
在 Java 中,32 位 int 的位索引范围为:左起最高位索引 31,右起最低位索引 0。对于 64 位 long,最高位索引则为 63。
左移生成掩码
通过左移运算符将值 1
移动到目标位置生成动态掩码:
int val = 0b0110_0100;
int pos = 2;
int mask = 1 << pos;
boolean isSet = (val & mask) > 0;
assertTrue(isSet);
这里 pos
可设为任意有效位位置。掩码生成后,通过按位与操作判断目标位是否置位。
左移数值本身
另一种思路是直接左移待测数值,使目标位移动到符号位(最高位)。利用二进制补码特性,通过判断结果是否为负数来确定符号位是否为 1:
int val = 0b0110_0100;
int pos = 2;
boolean isSet = ((val << (31 - pos)) < 0);
assertTrue(isSet);
当 pos=2
时,计算 31-2=29
,将原值左移 29 位后,目标位到达符号位。若结果为负数则表示该位已置位。
右移数值
同样可以使用右移运算符:将目标位移到最低位后,与掩码 1
进行按位与操作:
int val = 0b0110_0100;
int pos = 2;
boolean isSet = ((val >> pos) & 1) == 1;
assertTrue(isSet);
优化位运算方案
在性能敏感场景下,可通过减少 CPU 指令数优化代码。基于位运算通常快于算术运算的特性,重写左移方案:
boolean isSet = ((val << (~pos & 31)) < 0);
核心逻辑未变,但用 (~pos & 31)
替代了 (31-pos)
。数学推导如下:
(31 - pos) = (31 - pos) & 31
= (31 + (-pos)) & 31
= (31 & 31) + ((-pos) & 31)
= (31 & 31) + ((~pos + 1) & 31)
= (31 & 31) + (~pos & 31) + (1 & 31)
= ((31 + 1) & 31) + (~pos & 31)
= (32 & 31) + (~pos & 31)
= 0 + (~pos & 31)
= (~pos & 31)
关键点在于:(-pos)
转换为 (~pos + 1)
是二进制补码的求负规则。进一步简化代码:
boolean isSet = ((val << ~pos) < 0);
JVM 会自动处理移位范围:int 的有效移位范围为 0-31,long 为 0-63。这种写法虽然简洁,但可读性稍差,团队协作时需谨慎使用。
使用 BigInteger
当处理超过 64 位的大数或追求代码可读性时,BigInteger
是理想选择。它提供 testBit
方法直接检查指定位:
int val = 0b0110_0100;
int pos = 2;
boolean isSet = BigInteger.valueOf(val).testBit(pos);
assertTrue(isSet);
总结
本文探讨了从整型值中提取特定位的多种方法:
- 基础方案:使用硬编码掩码进行按位与操作
- 动态掩码:通过左移运算符生成动态掩码
- 数值移位:左移或右移数值本身,配合符号位或低位掩码判断
- 性能优化:利用位运算特性减少 CPU 指令
- 大数处理:使用 BigInteger 的 testBit 方法
选择方案时需权衡性能需求与代码可读性。对于高频运算场景,优化后的位运算方案更高效;而处理大数或追求可维护性时,BigInteger 是更安全的选择。