1. Overview
EnumMap is a Map implementation that exclusively takes Enum as its keys.
In this tutorial, we’ll discuss its properties, common use cases and when we should use it.
2. Project Setup
Imagine a simple requirement where we need to map days of the week with the sport we play on that day:
Monday Soccer
Tuesday Basketball
Wednesday Hiking
Thursday Karate
For this, we could use an enum:
public enum DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
Which we’ll see soon will be the key for our map.
3. Creation
To start exploring EnumMap, first we’ll need to instantiate one:
EnumMap<DayOfWeek, String> activityMap = new EnumMap<>(DayOfWeek.class);
activityMap.put(DayOfWeek.MONDAY, "Soccer");
And here is our first difference to something more common, like HashMap. Note that with HashMap, the type parameterization is sufficient, meaning we can get away with new HashMap<>(). However, EnumMap requires the key type in the constructor.
3.1. EnumMap Copy Constructor
EnumMap also ships with two copy constructors. The first takes another 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<>(dayMap);
assertThat(activityMapCopy.size()).isEqualTo(2);
assertThat(activityMapCopy.get(DayOfWeek.MONDAY)).isEqualTo("Soccer");
assertThat(activityMapCopy.get(DayOfWeek.TUESDAY)).isEqualTo("Basketball");
3.2. Map Copy Constructor
Or, if we have a non-empty Map whose key is an enum, then we can do that, too:
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");
Note that the map must be non-empty so that EnumMap can determine the key type from an existing entry.
If the specified map contains more than one enum type, the constructor will throw ClassCastException.
4. Adding and Retrieving Elements
After instantiating an EnumMap, we can add our sport using the put() method:
activityMap.put(DayOfWeek.MONDAY, "Soccer");
And to retrieve it, we can use get():
assertThat(activityMap.get(DayOfWeek.MONDAY)).isEqualTo("Soccer");
5. Checking for Elements
To check if we have a mapping defined for a particular day, we use containsKey():
activityMap.put(DayOfWeek.WEDNESDAY, "Hiking");
assertThat(activityMap.containsKey(DayOfWeek.WEDNESDAY)).isTrue();
And, to check if a particular sport is mapped to any key we use containsValue():
assertThat(activityMap.containsValue("Hiking")).isTrue();
5.1. null as Value
Now, null is a semantically valid value for EnumMap.
Let’s associate null with “doing nothing”, and map it to Saturday:
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. Removing Elements
In order to unmap a particular day, we simply remove() it:
activityMap.put(DayOfWeek.MONDAY, "Soccer");
assertThat(activityMap.remove(DayOfWeek.MONDAY)).isEqualTo("Soccer");
assertThat(activityMap.containsKey(DayOfWeek.MONDAY)).isFalse();
As we can observe, remove(key) returns the previous value associated with the key, or null if there was no mapping for the key.
We can also choose to unmap a particular day only if that day is mapped to a particular activity:
activityMap.put(DayOfWeek.Monday, "Soccer");
assertThat(activityMap.remove(DayOfWeek.Monday, "Hiking")).isEqualTo(false);
assertThat(activityMap.remove(DayOfWeek.Monday, "Soccer")).isEqualTo(true);
remove(key, value) removes the entry for the specified key only if the key is currently mapped to the specified value.
7. Collection Views
Just like with ordinary maps, with any EnumMap, we can have 3 different views or sub-collections.
First, let’s create a new map of our activities:
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
The first view of our activity map is values() which, as the name suggests, returns all the values in the map:
Collection values = dayMap.values();
assertThat(values)
.containsExactly("Soccer", "Hiking", "Karate");
Note here that EnumMap is an ordered map. It uses the order of the DayOfWeek enum to determine the order of the entries.
7.2. keySet
Similarly, keySet() returns a collection of the keys, again in enum order:
Set keys = dayMap.keySet();
assertThat(keys)
.containsExactly(DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY, DayOfWeek.SATURDAY);
7.3. entrySet
Lastly, entrySet() returns the mapping in pairs of key and value:
assertThat(dayMap.entrySet())
.containsExactly(
new SimpleEntry(DayOfWeek.MONDAY, "Soccer"),
new SimpleEntry(DayOfWeek.WEDNESDAY, "Hiking"),
new SimpleEntry(DayOfWeek.THURSDAY, "Karate")
);
Ordering in a map can certainly come in handy, and we go into more depth in our tutorial that compares TreeMap to HashMap.
7.4. Mutability
Now, remember that any changes we make in the original activity map will be reflected in any of its views:
activityMap.put(DayOfWeek.TUESDAY, "Basketball");
assertThat(values)
.containsExactly("Soccer", "Basketball", "Hiking", "Karate");
And vice-versa; any changes we make the sub-views will be reflected in the original activity map:
values.remove("Hiking");
assertThat(activityMap.containsKey(DayOfWeek.WEDNESDAY)).isFalse();
assertThat(activityMap.size()).isEqualTo(3);
Per EnumMap‘s contract with Map interface, the sub-views are backed by the original map.
8. When to Use EnumMap
8.1. Performance
Using Enum as key makes it possible to do some extra performance optimization, like a quicker hash computation since all possible keys are known in advance.
The simplicity of having enum as key means EnumMap only need to be backed up by a plain old Java Array with very simple logic for storage and retrieval. On the other hand, generic Map implementations need to cater for concerns related to having a generic object as its key. For example, HashMap needs a complex data structure and a considerably more complex storing and retrieval logic to cater for the possibility of hash collision.
8.2. Functionality
Also, as we saw, EnumMap is an ordered map, in that its views will iterate in enum order. To get similar behavior for more complex scenarios, we can look at TreeMap or LinkedHashMap.
9. Conclusion
In this article, we’ve explored the EnumMap implementation of the Map interface. When working with Enum as a key, EnumMap can come in handy.
The full source code for all the examples used in this tutorial can be found in the GitHub project.