应优先使用EnumSet和EnumMap替代普通集合,因其底层基于位向量和数组索引实现O(1)操作、零哈希开销、内存紧凑且类型安全;但需注意序列化兼容性、线程不安全及null约束。

直接用 EnumSet 和 EnumMap 替代普通 Set/Map,当键或元素是枚举类型时,性能提升明显——底层不走哈希计算、不用对象包装、内存紧凑、操作常数时间。
EnumSet:用位向量存枚举,省空间又快
它只接受同一枚举类的实例,内部用 long 或 long[] 当“开关灯”式位图存储(比如一个 long 的 64 位对应最多 64 个枚举常量)。添加、查找、遍历全是 O(1),比 HashSet 少了哈希计算、对象头、链表跳转开销。
- 必须用静态工厂方法创建:
EnumSet.noneOf(Color.class)、EnumSet.of(Color.RED, Color.BLUE)、EnumSet.allOf(Weekday.class) - 元素少(≤64)时自动用
RegularEnumSet(单个 long),超 64 个才切到JumboEnumSet(long[] 数组) - 避免在循环里反复调用
of()——每次新建对象;高频构建改用noneOf() + add()复用实例 - 不支持 null,混入非枚举类型或不同枚举类会编译报错,类型安全从源头保障
EnumMap:数组索引代替哈希,查得稳、排得准
它把 key 的 ordinal() 当数组下标,vals[ordinal] 直接取值。没有哈希冲突、不扩容、不装箱,插入和查询都是真· O(1)。而且遍历顺序永远等于枚举声明顺序,不用额外排序。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 构造必须传具体枚举类:
new EnumMap<Status, String>(Status.class),不能传变量或父类 - key 不可为 null(否则抛
NullPointerException),value 可为 null,但注意get()返回 null 时无法区分“不存在”还是“显式设为 null” - 高频场景建议用
computeIfAbsent(key, k -> defaultValue)替代get() + put()判断逻辑 - 不要用
keys()(过时的 Dictionary 方法),用keySet()或entrySet()遍历
序列化与升级要小心
两者都可序列化,但反序列化依赖枚举 values() 的顺序不变。如果上线后删了中间枚举常量,旧数据反序列化可能丢元素或错位——不是功能问题,而是 ordinal 偏移导致的静默错误。
立即学习“Java免费学习笔记(深入)”;
- 跨服务传输或持久化前,优先转成 List 或 JSON(如 Jackson 默认转枚举 name 字符串)
- 线上升级枚举类前,确认所有已序列化的数据已过期或完成迁移
- 单元测试里可模拟删枚举项再反序列化,提前暴露兼容性风险
多线程必须加锁
EnumSet 和 EnumMap 都不是线程安全的。没内置同步,也没提供 Collections.synchronizedXXX() 包装器。共享使用时,需外层加锁或用 ConcurrentHashMap + 枚举 key 作为折中方案。
- 读多写少场景:考虑用
CopyOnWriteArraySet(针对枚举集合)或不可变包装(如ImmutableSet.copyOf()) - 写频繁场景:直接上
synchronized块,或改用并发容器 + 枚举字符串 key(牺牲一点类型安全换线程安全)


















