EnumMap 必须用具体枚举类构造(如 Color.class),依赖编译期确定键集、运行时按 ordinal 索引数组实现高效存取;不接受 null 或非法键,遍历有序但不可用于自定义排序,视图集合只读。

直接用 EnumMap 替代 HashMap 处理枚举键,不是“换一个类名”那么简单,而是要匹配它的设计前提和使用边界——它快,是因为编译期就锁死了键的集合;它稳,是因为类型系统全程兜底。
必须用具体枚举类构造,不能靠泛型推断
EnumMap 构造时必须显式传入 枚举类字面量,例如 new EnumMap<Color, String>(Color.class)。这一步不是形式主义:JVM 会根据 Color.values().length 预分配固定长度的 Object[] 数组,后续所有 get/put 都直接按 key.ordinal() 索引访问。
以下写法是错的:
-
Class<? extends Enum> clazz = Color.class; new EnumMap(clazz)—— 编译通过,但运行时可能抛IllegalArgumentException(尤其在模块化或反射场景) -
new EnumMap<Enum<?>, String>(...)或通配符泛型 —— 编译不通过 - 用非枚举类(如
String、Integer或自定义常量类)假装“模拟枚举”——底层数组索引失效,优化归零
键只能是该枚举的有效实例,null 和非法值立刻报错
EnumMap 不接受 null 键,也不接受不属于目标枚举类的任何对象。这不是防御性编程问题,而是设计契约:
-
map.put(null, "x")→NullPointerException -
map.put(OtherEnum.VALUE, "x")→ClassCastException - 用反射强行 new 出枚举实例(JVM 禁止)→ 实际不会发生,但别尝试绕过检查
好处是:你永远不用写 if (key == null) 或做 instanceof 判定;坏处是:如果业务上真需要“空状态”,得用枚举里的某个占位常量(如 UNKNOWN),而不是 null。
遍历天然有序,但别把它当业务逻辑依赖
EnumMap 的 keySet()、entrySet() 迭代顺序严格对应枚举声明顺序(即 ordinal() 升序)。这是数组下标遍历的自然结果,不是额外排序。
适合场景:
- 生成配置项下拉列表(顺序稳定,前端可省去 sort)
- 状态码翻译表(
PENDING → "处理中",COMPLETED → "已完成",顺序即流程阶段)
风险点:
- 若代码隐含“第一个 key 就是默认值”,后来在枚举开头加了
INITIAL,逻辑就偏了 - 不要用它替代
TreeMap做自定义排序——EnumMap 没有sortedKeySet(),也不支持 Comparator
集合视图只读,结构性修改需格外小心
keySet()、values()、entrySet() 返回的是“视图”,不是副本:
-
keySet().add(...)、values().clear()→UnsupportedOperationException -
keySet().remove(color)是允许的,等价于map.remove(color) - 若需真正可变集合(比如边遍历边删多个 key),必须显式复制:
new ArrayList<>(map.keySet())
注意:迭代过程中混用 map.put() 和 keySet().iterator() 可能跳过新 entry 或重复遍历旧 entry——因为视图不保证强一致性,这是设计取舍,不是 bug。


















