1. 概述
EnumMap
是 Java 中 Map
接口的一个实现类,它只接受枚举类型(enum
)作为键。
本文将带你深入了解 EnumMap
的特性、使用场景以及为什么在某些情况下它是更好的选择。
2. 场景准备
假设我们有一个简单的需求:将一周中的每一天映射到当天进行的运动项目上:
Monday Soccer
Tuesday Basketball
Wednesday Hiking
Thursday Karate
为此我们可以定义一个枚举:
public enum DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
这个枚举将成为我们后续 EnumMap
的 key 类型。
3. 创建 EnumMap
要开始使用 EnumMap
,首先需要实例化它:
EnumMap<DayOfWeek, String> activityMap = new EnumMap<>(DayOfWeek.class);
activityMap.put(DayOfWeek.MONDAY, "Soccer");
这里和常见的 HashMap
有个关键区别:**EnumMap
构造时必须传入枚举类型的 class 对象**。而 HashMap
只需写成 new HashMap<>()
即可。
3.1. EnumMap 的拷贝构造器
EnumMap
提供了两个拷贝构造函数。
第一个是传入另一个 EnumMap
:
EnumMap<DayOfWeek, String> activityMap = new EnumMap<>(DayOfWeek.class);
activityMap.put(DayOfWeek.MONDAY, "Soccer");
activityMap.put(DayOfWeek.TUESDAY, "Basketball");
EnumMap<DayOfWeek, String> activityMapCopy = new EnumMap<>(activityMap);
assertThat(activityMapCopy.size()).isEqualTo(2);
assertThat(activityMapCopy.get(DayOfWeek.MONDAY)).isEqualTo("Soccer");
assertThat(activityMapCopy.get(DayOfWeek.TUESDAY)).isEqualTo("Basketball");
3.2. Map 的拷贝构造器
或者,如果你有一个非空的普通 Map,且其 key 是枚举类型,也可以直接构造:
Map<DayOfWeek, String> ordinaryMap = new HashMap<>();
ordinaryMap.put(DayOfWeek.MONDAY, "Soccer");
EnumMap<DayOfWeek, String> enumMap = new EnumMap<>(ordinaryMap);
assertThat(enumMap.size()).isEqualTo(1);
assertThat(enumMap.get(DayOfWeek.MONDAY)).isEqualTo("Soccer");
⚠️ 注意:传入的 Map 必须是非空的,否则 EnumMap
就无法推断 key 类型。
如果 Map 中包含多个不同枚举类型,则会抛出 ClassCastException
。
4. 添加与获取元素
创建好 EnumMap
后,我们可以通过 put()
方法添加键值对:
activityMap.put(DayOfWeek.MONDAY, "Soccer");
获取值则使用 get()
方法:
assertThat(activityMap.get(DayOfWeek.MONDAY)).isEqualTo("Soccer");
5. 判断元素是否存在
要判断某个枚举值是否已被映射,使用 containsKey()
:
activityMap.put(DayOfWeek.WEDNESDAY, "Hiking");
assertThat(activityMap.containsKey(DayOfWeek.WEDNESDAY)).isTrue();
要判断某个值是否被映射过,使用 containsValue()
:
assertThat(activityMap.containsValue("Hiking")).isTrue();
5.1. 支持 null 值
✅ EnumMap
允许 value 为 null
,语义上可以表示“无活动”。
assertThat(activityMap.containsKey(DayOfWeek.SATURDAY)).isFalse();
assertThat(activityMap.containsValue(null)).isFalse();
activityMap.put(DayOfWeek.SATURDAY, null);
assertThat(activityMap.containsKey(DayOfWeek.SATURDAY)).isTrue();
assertThat(activityMap.containsValue(null)).isTrue();
6. 删除元素
删除某个键对应的映射关系,使用 remove(key)
:
activityMap.put(DayOfWeek.MONDAY, "Soccer");
assertThat(activityMap.remove(DayOfWeek.MONDAY)).isEqualTo("Soccer");
assertThat(activityMap.containsKey(DayOfWeek.MONDAY)).isFalse();
返回值是被删除的 value,如果没有映射则返回 null
。
也可以选择性删除:只有当 key 映射到指定值时才删除:
activityMap.put(DayOfWeek.MONDAY, "Soccer");
assertThat(activityMap.remove(DayOfWeek.MONDAY, "Hiking")).isFalse();
assertThat(activityMap.remove(DayOfWeek.MONDAY, "Soccer")).isTrue();
remove(key, value)
方法只有在 key 当前映射到 value 时才会删除。
7. 集合视图
和普通 Map 一样,EnumMap
也提供三种集合视图:
先创建一个活动映射:
EnumMap<DayOfWeek, String> activityMap = new EnumMap<>(DayOfWeek.class);
activityMap.put(DayOfWeek.THURSDAY, "Karate");
activityMap.put(DayOfWeek.WEDNESDAY, "Hiking");
activityMap.put(DayOfWeek.MONDAY, "Soccer");
7.1. values()
返回所有 value 的集合:
Collection<String> values = activityMap.values();
assertThat(values).containsExactly("Soccer", "Hiking", "Karate");
✅ 注意:EnumMap
是有序的,其顺序与枚举常量定义顺序一致。
7.2. keySet()
返回所有 key 的集合,同样保持枚举顺序:
Set<DayOfWeek> keys = activityMap.keySet();
assertThat(keys)
.containsExactly(DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY);
7.3. entrySet()
返回键值对集合:
assertThat(activityMap.entrySet())
.containsExactly(
new SimpleEntry<>(DayOfWeek.MONDAY, "Soccer"),
new SimpleEntry<>(DayOfWeek.WEDNESDAY, "Hiking"),
new SimpleEntry<>(DayOfWeek.THURSDAY, "Karate")
);
7.4. 视图的可变性
对原 map 的修改会反映在视图中:
activityMap.put(DayOfWeek.TUESDAY, "Basketball");
assertThat(values).containsExactly("Soccer", "Basketball", "Hiking", "Karate");
反之,对视图的修改也会反映回原 map:
values.remove("Hiking");
assertThat(activityMap.containsKey(DayOfWeek.WEDNESDAY)).isFalse();
assertThat(activityMap.size()).isEqualTo(3);
✅ 根据 Map
接口规范,这些视图是由原 map 支持的,不是独立副本。
8. 何时使用 EnumMap
8.1. 性能优势
由于 key 是枚举类型,所有可能的 key 都是已知的,因此可以进行优化:
- hash 计算更高效
- 内部实现基于数组,逻辑简单
- 不会出现 hash 冲突,避免了
HashMap
那种复杂的数据结构
8.2. 功能优势
- ✅ 保持枚举顺序,适合需要排序的场景
- 比
HashMap
更安全,key 类型严格限定 - 比
TreeMap
更轻量,不需要比较器
9. 总结
当你需要使用枚举作为 Map 的 key 时,EnumMap
是一个简单粗暴又高效的选择。它不仅性能优越,还提供了良好的语义支持和顺序保障。
所有示例代码可在 GitHub 项目 中找到。