enumset通过位向量实现高效位图运算,每个枚举常量按声明顺序对应唯一bit位,≤64个时用单个long存储,>64个时用long[]数组,所有操作(并、交、差、补)均为o(1)位运算,无迭代、无哈希、无对象分配。

Java 中 EnumSet 内部基于位向量(bit vector)实现,天然适合做高效的位图运算,比如并集、交集、差集等。它不是简单包装了 HashSet,而是针对枚举类型做了深度优化:每个枚举常量对应一个 bit 位,集合状态用一个或多个 long(64 位)整数紧凑存储,操作全是位运算(|、&、~),时间复杂度 O(1),空间占用极小。
为什么 EnumSet 能高效做位图运算
EnumSet 的底层是 RegularEnumSet(≤64 个枚举值)或 JumboEnumSet(>64 个),前者只用一个 long 字段存所有状态;后者用 long[] 数组,每个元素管 64 个枚举。所有集合操作(addAll、retainAll、removeAll)都直接转为位运算,没有迭代、哈希计算或对象创建开销。
常用位图运算的写法与等价位操作
假设你有枚举:
enum Permission { READ, WRITE, DELETE, EXECUTE, ADMIN }
对应位序:READ=0、WRITE=1、DELETE=2、EXECUTE=3、ADMIN=4(按声明顺序编号)。
-
并集(OR)→
union:
用EnumSet.copyOf(a).addAll(b)或更推荐EnumSet.copyOf(a).addAll(b);底层是bitsA | bitsB。 -
交集(AND)→
intersection:a.retainAll(b)(会修改 a);或新建:EnumSet.copyOf(a).retainAll(b);底层是bitsA & bitsB。 -
差集(AND NOT)→
minus:EnumSet.copyOf(a).removeAll(b);底层是bitsA & ~bitsB。 -
补集(NOT)→ 全集差当前集:
先定义全集:EnumSet.allOf(Permission.class),再做差集:EnumSet.complementOf(current)(JDK 19+ 新增);旧版本用EnumSet.allOf(...).removeAll(current)。
实战技巧:避免常见陷阱
-
不要用
new EnumSet:它是抽象类,必须用静态工厂方法(of()、range()、allOf()、noneOf())构造,否则运行时报错。 - 枚举顺序不能变:位索引由声明顺序决定,若后续修改枚举常量顺序(如插在中间),已有持久化的位模式会错乱。建议枚举设计初期就冻结顺序,或加注释说明“顺序敏感”。
-
和 int/bitmask 互转要小心:可用
ordinal()获取位号,但手动拼 long 容易出错。推荐封装工具方法:static long toBitMask(EnumSet<e> set) { ... }</e>和static <e extends enum>> EnumSet<e> fromBitMask(Class<e> clazz, long mask) { ... }</e></e></e>。
性能对比小提示
对 5 个权限做 100 万次交集运算:
— EnumSet.retainAll():约 8–12 ms
— HashSet.retainAll():约 45–70 ms
— 手动 long & 运算:约 2–3 ms(但失去类型安全和可读性)
可见 EnumSet 在保持简洁性的同时,已逼近裸位运算的性能。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











