BitSet.andNot 是实现集合差集(a − b)最直接高效的方式:调用后 a 中在 b 为 true 的位被清零,原地修改 a 且自动扩容,时间复杂度 O(n/64),远优于 ArrayList 或 HashSet 差集。

BitSet.andNot 是实现集合差集(a − b)最直接、最高效的方式:调用 a.andNot(b) 后,a 中所有在 b 中为 true 的位会被清零,结果即为“属于 a 但不属于 b”的元素集合。
andNot 的语义与行为
该方法执行的是集合差集运算,不是底层 CPU 位指令的简单按位与非。它等价于数学中的 a \ b(相对补集),即保留 a 中存在、b 中不存在的元素。
- 操作是原地修改:a 被直接改变,b 不受影响
- 若 b 的某一位超出 a 当前逻辑范围(如 b.set(1000),但 a 尚未扩展到第 1000 位),a 会自动扩容以覆盖该索引,再执行清除
- 不抛出 IndexOutOfBoundsException(除非传入负索引),对高位自动补 false 处理,安全可靠
使用前的常见准备
为避免误改原始数据,多数场景需先复制一份:
- Java 中用
new BitSet(original)或original.clone() - Go 的 bitset 库通常提供
Copy()方法;C++ bitset 固定大小,需手动构造新实例并赋值 - 若只读需求频繁,建议封装不可变包装或选用 ImmutableBitSet(如 Guava)
对比其他差集实现方式
相比传统集合操作,andNot 在时空效率上优势明显:
- ArrayList.removeAll(Collection):最坏 O(n×m),百万级数据可能超时甚至失败
- HashSet 差集:O(n) 时间但需额外 O(m) 内存存储哈希表,且字符串/对象比较开销大
- BitSet.andNot:O(n/64) 时间(按 long 块处理),内存仅约 n/8 字节,无对象哈希与 equals 开销
典型使用示例(Java)
假设有用户 ID 集合 a(已注册用户)、b(VIP 用户),求普通注册用户:
BitSet allUsers = new BitSet(); allUsers.set(1); allUsers.set(5); allUsers.set(10); // ID 1,5,10 BitSet vipUsers = new BitSet(); vipUsers.set(5); // ID 5 是 VIP BitSet regularUsers = (BitSet) allUsers.clone(); regularUsers.andNot(vipUsers); // 结果含 ID 1 和 10










