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(clubMap.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.