Java EnumMap的指南
1.概述
EnumMap是一个Map 实现,它只接受Enum作为它的键。
在本教程中,我们将讨论它的属性、常见的使用情况以及我们应该在什么时候使用它。
2.项目设置
想象一下一个简单的要求,我们需要将一周中的几天与我们在那一天进行的运动项目进行映射。
Monday Soccer
Tuesday Basketball
Wednesday Hiking
Thursday Karate
为此,我们可以使用一个枚举来实现。
public enum DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
我们很快就会看到,这将是我们map的键。
3.创建
要开始探索EnumMap,首先,我们需要实例化一个。
EnumMap<DayOfWeek, String> activityMap = new EnumMap<>(DayOfWeek.class);
activityMap.put(DayOfWeek.MONDAY, "Soccer");
这里是我们与更常见的东西的第一个区别,比如HashMap。请注意,对于HashMap,类型参数化是足够的,这意味着我们可以用new HashMap<>()来解决。但是,但是,EnumMap 需要在构造函数中输入键的类型。
3.1.EnumMap Copy构造函数
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 Copy 构造函数
或者,如果我们有一个非空的Map,它的键是一个枚举,那么我们也可以这样做:
Map<DayOfWeek, String> ordinaryMap = new HashMap();
ordinaryMap.put(DayOfWeek.MONDAY, "Soccer");
EnumMap enumMap = new EnumMap(ordinaryMap);
assertThat(enumMap.size()).isEqualTo(1);
assertThat(enumMap.get(DayOfWeek.MONDAY)).isEqualTo("Soccer");
请注意,该map必须是非空的,以便EnumMap能够从现有的条目中确定键的类型。
如果指定的映射包含一个以上的枚举类型,构造函数将抛出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为值
现在,null是EnumMap的一个语义上有效的值。
让我们把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()它。
activityMap.put(DayOfWeek.MONDAY, "Soccer");
assertThat(activityMap.remove(DayOfWeek.MONDAY)).isEqualTo("Soccer");
assertThat(activityMap.containsKey(DayOfWeek.MONDAY)).isFalse();
我们可以看到,remove(key)返回与键相关联的前一个值,或者null如果没有键的映射。
我们也可以选择取消对某一天的映射只有当这一天被映射到某个特定的活动时才可以:。
activityMap.put(DayOfWeek.Monday, "Soccer");
assertThat(activityMap.remove(DayOfWeek.Monday, "Hiking")).isEqualTo(false);
assertThat(activityMap.remove(DayOfWeek.Monday, "Soccer")).isEqualTo(true);
remove(key, value)只有当键当前被映射到指定的值时,才会删除指定的键的条目。
7.集合视图
就像普通的地图一样,对于任何EnumMap,我们可以有3个不同的视图或子集合。
首先,让我们为我们的活动创建一个新的地图。
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() ,顾名思义,它返回地图中的所有值。
Collection values = dayMap.values();
assertThat(values)
.containsExactly("Soccer", "Hiking", "Karate");
请注意,EnumMap 是一个有序的地图。它使用DayOfWeek enum的顺序来确定条目的顺序。
7.2. 键集
类似地,keySet()返回一个键的集合,同样是按照枚举的顺序。
Set keys = dayMap.keySet();
assertThat(keys)
.containsExactly(DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY, DayOfWeek.SATURDAY);
7.3. 条目集
最后,entrySet()以key和value成对的方式返回映射。
assertThat(dayMap.entrySet())
.containsExactly(
new SimpleEntry(DayOfWeek.MONDAY, "Soccer"),
new SimpleEntry(DayOfWeek.WEDNESDAY, "Hiking"),
new SimpleEntry(DayOfWeek.THURSDAY, "Karate")
);
在地图中排序当然可以派上用场,我们在比较TreeMap和HashMap的教程中进行了更深入的介绍。
7.4.可变性
现在,请记住,我们在原始活动图中所做的任何改变都会反映在它的任何一个视图中。
activityMap.put(DayOfWeek.TUESDAY, "Basketball");
assertThat(values)
.containsExactly("Soccer", "Basketball", "Hiking", "Karate");
反之亦然;我们对子视图所做的任何改变都会反映在原始活动图中。
values.remove("Hiking");
assertThat(activityMap.containsKey(DayOfWeek.WEDNESDAY)).isFalse();
assertThat(activityMap.size()).isEqualTo(3);
根据EnumMap与Map界面的约定,子视图是由原始地图支持的。
8.何时使用EnumMap
8.1.性能
使用 Enum 作为键可以进行一些额外的性能优化,例如更快的哈希计算,因为所有可能的键都是预先知道的。
将enum作为键的简单性意味着EnumMap只需要由一个普通的Java Array 来支持,存储和检索的逻辑非常简单。另一方面,通用的Map 实现需要照顾到与将通用对象作为其键相关的问题。例如,HashMap 需要一个复杂的数据结构和一个相当复杂的存储和检索逻辑,以满足哈希碰撞的可能性。
8.2.功能性
另外,正如我们所看到的,EnumMap 是一个有序的地图,因为它的视图将以枚举的顺序进行迭代。为了在更复杂的情况下获得类似的行为,我们可以看看TreeMap或LinkedHashMap。
9.结语
在这篇文章中,我们已经探讨了EnumMap对Map接口的实现。当使用Enum作为一个键时,EnumMap可以派上用场。
本教程中使用的所有示例的完整源代码可以在GitHub项目中找到。