BitSet比boolean[]更省内存,因前者每布尔值占1bit,后者通常占1字节;存100万布尔值时前者约125KB,后者至少1MB,但小数据量时原生数组更快。

BitSet 为什么比 boolean[] 更省内存
因为 BitSet 每个布尔值只占 1 bit,而 boolean[] 在 JVM 中通常按 byte 对齐(多数实现里每个元素占 1 字节),存 100 万个布尔值,boolean[1000000] 至少占 1MB,BitSet 只要约 125KB。
但别一上来就换——BitSet 是对象,有额外开销;小数据量(比如几百位)反而不如原生数组快。
- 适合场景:
BitSet真正优势在稀疏标记、超大范围位操作(如记录 1~10⁷ 的质数、用户 ID 是否在线) - 注意:它不支持泛型,不能直接放进
ArrayList这类结构做类型推导 - 底层用
long[]存储,所以实际容量总是 64 的倍数,length()返回的是最高位索引+1,不是已设置位数
set()、get()、flip() 参数容易搞反的坑
set(int index) 是把第 index 位置为 true,不是“设成某个值”;get(int index) 越界不会抛异常,而是返回 false——这很危险,尤其当误算索引时会静默失败。
-
set(5, true)和set(5)效果一样;但set(5, false)才是清零,不是clear(5)(后者也合法,但语义更明确) -
flip(3)是取反,不是“翻转整个集合”,想全取反得用flip(0, size) - 所有带 range 的方法(如
get(from, to))都是左闭右开:get(0, 8)返回前 8 位的新BitSet,不含第 8 位
and()、or()、xor() 不是线程安全的
这些批量位运算方法会直接修改调用方对象,而且不加锁。多线程并发调用 a.and(b) 会导致结果不可预测,甚至 ArrayIndexOutOfBoundsException(因内部 words 数组被 resize 中断)。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 如果必须并发,要么加外部同步(
synchronized(bitset)),要么每次运算前 clone:result = (BitSet) a.clone(); result.and(b); -
andNot(BitSet set)常被误以为是“减法”,其实是 a = a & (~b),注意 b 里为 false 的位对 a 没影响 - 性能提示:
or()比and()快一点,因为前者可以提前终止(遇到全 0 long 就跳过),后者不行
和 Java 8+ Stream 配合的现实限制
想把 BitSet 当集合遍历?别指望 stream() 方法——它压根没实现 Collection 接口,也没有默认的 stream()。你得手动转成 IntStream:
bitSet.stream().forEach(i -> System.out.println("bit " + i + " is set"));
但注意:这个 stream() 是 Java 8 新增的实例方法,不是 Stream API 的扩展;它返回的是 IntStream,只遍历置位的索引,不是所有索引。
- 如果需要“所有索引”的流(包括 false 的),只能手写循环:
for (int i = 0; i -
cardinality()是 O(n) 的,别在热循环里反复调用;真要频繁查数量,自己维护一个计数器 - 序列化兼容性:JDK 14+ 对
BitSet序列化格式做了优化,老版本反序列化新版本保存的字节可能失败
BitSet 的边界行为太多,比如 nextSetBit(-1) 返回 0,nextClearBit(0) 在空 BitSet 上返回 0——这些细节不查源码或文档,光靠试错很容易掉进逻辑黑洞。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










