1. 概述
在编程中,位运算是一种底层操作,用于直接操纵内存中数值的二进制位。这类操作包括按位逻辑运算、移位操作和位掩码(bitmask)处理。
Kotlin 提供了丰富的整数位操作函数,本文将系统梳理这些功能,并结合实际示例说明其用法。✅ 掌握这些技巧对性能敏感场景(如算法优化、网络协议解析)非常有帮助,属于高级开发必备技能。
⚠️ 注意:本文涉及的操作对象主要是 Kotlin 中的整型(Int, Long 等),默认以补码形式存储。
参考文档:Kotlin 数值类型
2. 按位逻辑运算
按位逻辑运算符会对两个整数的二进制表示逐位进行比较,生成新的整数值。
2.1 and 运算符
and
是最基本的按位与操作。只有当两个对应位都为 1
时,结果位才为 1
,否则为 0
。
@Test
fun `applies and bit operator on two integers`() {
val a = 0b00011110
val b = 0b01111000
assertEquals(0b00011000, a and b)
}
📌 踩坑提醒:不要把 and
和布尔逻辑 &&
混淆。and
是位运算,&&
是条件判断。
2.2 or 运算符
or
表示按位或。只要任意一个操作数的对应位为 1
,结果位就为 1
。
@Test
fun `applies or bit operator on two integers`() {
val a = 0b00011110
val b = 0b01111000
assertEquals(0b01111110, a or b)
}
常用于“设置某一位”场景,例如权限标记合并。
2.3 xor 运算符
xor
是按位异或。当两个位不同时结果为 1
,相同时为 0
。
@Test
fun `applies xor bit operator on two integers`() {
val a = 0b00011110
val b = 0b01111000
assertEquals(0b01100110, a xor b)
}
✅ 典型用途:
- 交换两个变量无需临时变量(
a ^= b; b ^= a; a ^= b
) - 判断两数是否符号不同
- 加密中的简单混淆
3. 位移运算
Kotlin 支持多种位移操作,常用于高效实现乘除 2 的幂次运算。
3.1 有符号与无符号整数
Kotlin 支持 有符号和无符号整数。理解这一点对正确使用右移至关重要。
- 有符号数:最高位(MSB)是符号位。
0
表示正数,1
表示负数。 - 无符号数:所有位都表示数值大小。
⚠️ 在做右移时,符号位是否扩展决定了行为差异。
3.2 左移 shl 操作符
shl
将二进制位向左移动指定位置,右侧补 0
。等价于乘以 $2^n$。
@Test
fun `shifts bits left in an integer`() {
assertEquals(512, 128 shl 2) // 128 * 4 = 512
}
✅ 提示:相比 * 4
,shl 2
性能更高,编译器通常会自动优化,但显式使用更语义化。
3.3 右移 shr 操作符
shr
是有符号右移,左侧根据原符号位填充(即“算术右移”)。等价于带符号的除以 $2^n$。
@Test
fun `shifts bits right in a positive integer`() {
assertEquals(32, 128 shr 2) // 128 / 4 = 32
}
处理负数时保留符号:
@Test
fun `shifts bits right in a negative integer`() {
val a = -128 // 11111111111111111111111110000000
val expected = -32 // 11111111111111111111111111100000
assertEquals(expected, a shr 2)
}
3.4 无符号右移 ushr 操作符
ushr
是无符号右移,无论原数正负,左侧一律补 0
(即“逻辑右移”)。
@Test
fun `shifts unsigned bits right in an integer`() {
val a = -128 // 11111111111111111111111110000000
val expected = 1073741792 // 00111111111111111111111111100000
assertEquals(expected, a ushr 2)
}
📌 关键区别:
shr
:保持符号,适合数学运算ushr
:强制视为无符号处理,适合二进制数据解析(如字节流)
4. 位操作方法
从 Kotlin 1.4.0 起,标准库引入了一系列实用的位操作函数,极大简化了位掩码操作。详见更新日志
本文示例统一使用 UByte
类型(8 位无符号整数,范围 0~255),便于观察低位行为。转换时取原值的低 8 位。
4.1 取反:inv() 方法
inv()
返回整数的按位取反结果,即 0→1
,1→0
。
@Test
fun `inverts bits in an integer`() {
val a = 0b00011110.toUByte()
assertEquals(0b11100001.toUByte(), a.inv())
}
相当于 xor 0xFF
,常用于掩码反转。
4.2 统计 1 的个数:countOneBits()
返回二进制表示中 1
的数量(又称“汉明重量”)。
@Test
fun `counts one bits in an integer`() {
val a = 0b00111110.toUByte()
assertEquals(5, a.countOneBits())
}
✅ 应用场景:校验和计算、稀疏性判断。
4.3 前导/后导零统计
countLeadingZeroBits()
:从最高位开始连续0
的个数countTrailingZeroBits()
:从最低位开始连续0
的个数
@Test
fun `counts leading and trailing zero bits in an integer`() {
val a = 0b00111110.toUByte()
assertEquals(2, a.countLeadingZeroBits()) // 开头两个0
assertEquals(1, a.countTrailingZeroBits()) // 结尾一个0
}
✅ 高效用途:快速估算对数、查找最低置位。
4.4 提取最高/最低位 1
takeHighestOneBit()
:返回仅包含最高位1
的数takeLowestOneBit()
:返回仅包含最低位1
的数
@Test
fun `takes highest and lowest one bit in an integer`() {
val a = 0b00111110.toUByte()
assertEquals(0b00100000, a.takeHighestOneBit()) // 第6位
assertEquals(0b00000010, a.takeLowestOneBit()) // 第2位
}
✅ 技巧:takeLowestOneBit()
等价于 x & -x
,常用于树状数组(Fenwick Tree)。
4.5 循环移位:rotateLeft/rotateRight
循环移位会把移出的位重新填入另一端,不会丢失信息。
@Test
fun `rotates bits left and right in an integer`() {
val a = 0b00111110.toUByte()
assertEquals(0b11110001.toUByte(), a.rotateLeft(3))
assertEquals(0b11000111.toUByte(), a.rotateRight(3))
}
✅ 适用场景:加密算法、哈希函数、随机数生成。
5. 总结
Kotlin 提供了一套完整且现代化的位操作 API,涵盖:
- ✅ 基础按位运算:
and
,or
,xor
- ✅ 三种右移:
shr
(算术)、ushr
(逻辑) - ✅ 实用位方法:计数、旋转、提取特定位
这些操作在底层开发、性能优化和算法题中极为常见。熟练掌握不仅能提升代码效率,还能增强对数据本质的理解。
所有示例代码已上传至 GitHub:Baeldung/kotlin-tutorials