EnumMap in Java

Introduction

An EnumMap in Java is a specialized map implementation for use with enum type as the keys. In this post, we will learn about an EnumMap in Java, when to use it and the methods in it with examples.

What is an EnumMap in Java

An EnumMap is a Map (implements the Map interface). It is meant to be used when we want to have a map whose keys will be enum constants from a single enum type. Hence, the keys must all come from a single enum type.

Why use an EnumMap?

Why use an EnumMap over a normal map with enum type as key?
In a normal map, the possible values for keys are unbounded. But in an EnumMap, the set of keys are limited, which is equal to the number of enum constants in the enum type. Hence, internally the EnumMap can optimize the way it stores the key-value mappings to improve the performance.

It uses an array to store the keys and values, which is the reason for the improved runtime performance over a normal map.

Creating an EnumMap

For the examples, let us use the below enum of colors.

public enum MyColor {
    RED,
    BLUE,
    GREEN
}

Let us create an EnumMap with the MyColor enum as the key and the Color class from java.awt as the value.

The EnumMap has three constructors to create an EnumMap in Java. Let us explore them.

Creating EnumMap with just the enum type

This is a straightforward way to create an EnumMap. We pass the Class type of the enum (Class<K>) to the constructor of the EnumMap. This will create an empty EnumMap.

EnumMap<MyColor, Color> colorEnumMap = new EnumMap<>(MyColor.class);

We can add entries to the map, as we do to a normal map.

colorEnumMap.put(MyColor.RED, Color.RED);
colorEnumMap.put(MyColor.BLUE, Color.BLUE);
System.out.println(colorEnumMap);

Prints,

{RED=java.awt.Color[r=255,g=0,b=0], BLUE=java.awt.Color[r=0,g=0,b=255]}

The format of the values of AWT colors printed is the default toString() implementation of the Java AWT Color class.

Creating EnumMap from another EnumMap

We can also create an EnumMap from an existing EnumMap. The newly created EnumMap will be initialized with the entries from the passed EnumMap (copied entries). Since we pass an EnumMap, it can get the key type from that.

EnumMap<MyColor, Color> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(MyColor.RED, Color.RED);

//Creates a new enum map from another existing enum map
EnumMap<MyColor, Color> newColorEnumMap = new EnumMap<>(colorEnumMap);
newColorEnumMap.put(MyColor.GREEN, Color.GREEN); //add new entry to the new map

System.out.println(newColorEnumMap);
System.out.println(colorEnumMap);

Prints,

{RED=java.awt.Color[r=255,g=0,b=0], GREEN=java.awt.Color[r=0,g=255,b=0]}
{RED=java.awt.Color[r=255,g=0,b=0]}

Once created, the colorEnumMap and newColorEnumMap are separate instances with their own data. Changes made to one will not affect the other.

Creating EnumMap from another Map

There is a constructor which accepts a general map as a parameter and builds an EnumMap from that. The condition is that the key of that map must be an enum.

This creates an enum map initialized from the passed map. The passed map must have at least one mapping to determine the enum map’s key type. If the passed map is empty, it throws an IllegalArgumentException.

Map<MyColor, Color> colorMap = new HashMap<>();
colorMap.put(MyColor.RED, Color.RED);
colorMap.put(MyColor.BLUE, Color.BLUE);

//Creates a new enum map from another existing map
Map<MyColor, Color> colorEnumMap = new EnumMap<>(colorMap);
System.out.println(colorEnumMap);
colorEnumMap.put(MyColor.GREEN, Color.GREEN);
System.out.println(colorEnumMap);

Prints,

{RED=java.awt.Color[r=255,g=0,b=0], BLUE=java.awt.Color[r=0,g=0,b=255]}
{RED=java.awt.Color[r=255,g=0,b=0], BLUE=java.awt.Color[r=0,g=0,b=255], 
    GREEN=java.awt.Color[r=0,g=255,b=0]}

Passing an empty map (HashMap here),

Map<MyColor, Color> colorMap = new HashMap<>();
colorEnumMap = new EnumMap<>(colorMap);

Throws,

java.lang.IllegalArgumentException: Specified map is empty

Inserting data into an enum map

We have already seen the put method to add data into an EnumMap. Let us see the putAll method.

The putAll method takes another map and copies all the mappings from it to the current map. If there are common keys between both maps, the one from the passed map will overwrite the existing value.

Let us create two enum maps and populate it with some data (Note: I’m using String as value here).

EnumMap<MyColor, String> colorEnumMap1 = new EnumMap<>(MyColor.class);
colorEnumMap1.put(MyColor.RED, "RED");
colorEnumMap1.put(MyColor.BLUE, "BLUE");

EnumMap<MyColor, String>  colorEnumMap2 = new EnumMap<>(MyColor.class);
colorEnumMap2.put(MyColor.RED, "RED-2");
colorEnumMap2.put(MyColor.BLUE, "BLUE-2");
colorEnumMap2.put(MyColor.GREEN, "GREEN-2");

System.out.println(colorEnumMap2); //{RED=RED-2, BLUE=BLUE-2}

Let us call putAll on the second EnumMap and pass the first. 

colorEnumMap2.putAll(colorEnumMap1);

System.out.println(colorEnumMap2); //{RED=RED, BLUE=BLUE, GREEN=GREEN-2}

Since the second enum map had a mapping for MyColor.RED, it will be replaced by the value in the first map. Hence, the second enum map will have value as RED for MyColor.RED.

Other methods to insert data

Other than these, since an EnumMap is a Map, we can use the methods available in a map like computecomputeIfPresentcomputeIfAbsent on an EnumMap.

Removing entries from an EnumMap

The remove method takes a key and removes the entry with that key (if present) from the map. It returns the previously value associated with the key or null if the key was absent (it could also mean that the key was mapped to null itself).

EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(MyColor.RED, "RED");
colorEnumMap.put(MyColor.BLUE, "BLUE");

System.out.println(colorEnumMap.remove(MyColor.RED)); //RED
System.out.println(colorEnumMap); //{BLUE=BLUE}

Conditional remove

There is another remove method (from Map interface) which performs conditional removal. In addition to passing a key, we can also pass a value. The removal will occur only if the passed value is the value mapped to the key.

This version of the remove method returns a boolean which denotes if the removal did happen or not.

EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(MyColor.RED, "RED");
colorEnumMap.put(MyColor.BLUE, "BLUE");

System.out.println(colorEnumMap.remove(MyColor.RED, "RED")); //true
System.out.println(colorEnumMap.remove(MyColor.BLUE, "WrongValue")); //false
System.out.println(colorEnumMap); //{BLUE=BLUE}

In the above example, since the enum MyColor.RED was mapped to the string RED, it was successfully removed. But for the key MyColor.BLUE, we passed a different value (MyColor.BLUE is mapped to String BLUE) and hence it wasn’t removed.

Getting/Querying for data from an EnumMap

Map#get method

The get method takes a key and returns the mapped value for that key. It will return null if the enum map has no mapping for that key (Or the key could be explicitly mapped to a null value).

EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(MyColor.RED, "RED");
colorEnumMap.put(MyColor.BLUE, "BLUE");

System.out.println(colorEnumMap.get(MyColor.RED)); //RED
System.out.println(colorEnumMap.get(MyColor.GREEN)); //null

containsKey and containsValue

The containsKey method takes a key as a parameter and returns true if the map has an entry for the passed key and false otherwise.

The containsValue method takes an object and returns true if the map has one or more keys mapped to the passed value. Note that it has to traverse the entire map for this and hence the time complexity of containsValue is O(n).

System.out.println(colorEnumMap.containsKey(MyColor.RED)); //true
System.out.println(colorEnumMap.containsKey(MyColor.GREEN)); //false
System.out.println(colorEnumMap.containsValue("RED")); //true
System.out.println(colorEnumMap.containsValue("RED-2")); //false

EnumMap Iteration order

An Enum map maintains the entries in the natural order of the keys i.e., since the keys are enums, it follows the order in which they are declared in the enum.

EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(MyColor.RED, "My-Red");
colorEnumMap.put(MyColor.BLUE, "My-Blue");

System.out.println(colorEnumMap); //{RED=My-Red, BLUE=My-Blue}

If we change the enum as,

public enum MyColor {
    BLUE,
    RED,
    GREEN
}

It would print,

{BLUE=My-Blue, RED=My-Red}

EnumMap Collection Views

There are three methods viz., entrySetkeySet and values which returns a view of the EnumMap. Any changes made to the enum map will be reflected in the views and vice-versa.

entrySet

The entrySet returns a Set view of the mappings (both key and value) of the enum map. The Set’s iterator will follow the natural ordering of the enum map.

keySet

The keySet returns a set view of the keys present in the map. The Set’s iterator will obey the ordering of the enum map.

values

The values method returns a Collection view of the values of the map. Again, the iterator will return the value as per the natural ordering of the enum map iterator.

EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(MyColor.RED, "RED");
colorEnumMap.put(MyColor.BLUE, "BLUE");

System.out.println(colorEnumMap.entrySet()); // [RED=My-Red, BLUE=My-Blue]
System.out.println(colorEnumMap.keySet()); // [RED, BLUE]
System.out.println(colorEnumMap.values()); // [My-Red, My-Blue]

Enum map and null keys

An EnumMap strictly requires the keys to be valid enum constants. Hence, it does not allow null keys. It will throw a NullPointerException if it encounters a null key.

EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(MyColor.class);
colorEnumMap.put(null, "a"); //throws NullPointerException
Map<MyColor, String> map = new HashMap<>();
map.put(null, "some-value"); // A HashMap allows null keys.

//Throws NullPointerException at creation time
EnumMap<MyColor, String> colorEnumMap = new EnumMap<>(map);

Conclusion

This concludes the post on the EnumMap in Java. We learnt why EnumMap is preferred over a normal map when we have enum type as the key. We saw the operations on an EnumMap with examples in this post.
Java also has EnumSet – We use an EnumSet when we need a Set of enum instances.

Other recommended related articles:

Leave a Reply