There is a special type of java.util.Map
that we can use when the keys are enums: java.util.EnumMap
.
It is my experience that developers are often unaware of the EnumMap
and will tend instead to use HashMap
or LinkedHashMap
even when they are using enums as keys.
Internal mechanisms
HashMap
is represented internally as a dynamically resized table of hash bin nodes. The keys must be hashed, and the get
and put
algorithms have a reasonable degree of complexity.
LinkedHashMap
extends HashMap
. LinkedHashMap
uses a hash table combined with a doubly-linked list to give a predictable iteration order. For this reason they have even more complexity than basic hash maps.
EnumMap
is based on the following fact: If we know the entire set of all possible keys of a Map in advance, then we can use a more efficient algorithm than if we need to support any arbitrary set of keys. Enum maps are represented internally as arrays, which allows for an efficient implementation.
Performance comparison
I decided to investigate the performance difference between the EnumMap
and these two kinds of hash map. The results clearly show that the EnumMap
has a significant performance advantage over hash maps. These performance advantages become more apparent the more values there are in the enum, and the more read-cycles we perform on the map instance.
Test approach
For this test I am using Java 17.
First, I defined four different enums, with the following number of values respectively: 3, 5, 10 and 20. These sizes I felt would give a broad sample of the typical size of enums.
I then wrote the following generic test function to test any type of enum with any type of map:
private static final int ITERATIONS = 1_000_000;
private static final int READ_CYCLES = 10;
// ...
private <M extends Map<E, Integer>, E extends Enum<E>> void testMapPerformance(
E[] values, Supplier<M> mapSupplier, String mapName) {
long total = 0;
Instant start = Instant.now();
for (int i = 0; i < ITERATIONS; i++) {
Map<E, Integer> map = mapSupplier.get();
for (int j = 0; j < values.length; j++) {
map.put(values[j], j);
}
for (int r = 0; r < READ_CYCLES; r++) {
for (E value : values) {
total += map.get(value);
}
}
}
Instant end = Instant.now();
// ...
}
The function times how long it takes to perform one million iterations of:
- create a new map;
- fill it with with each enum value as the map entry key and the value’s index as the map entry value; and
- read each value from the map a set number of times as defined by the
READ_CYCLES
constant.
I then wrote a dozen test methods, four for each of the different map types EnumMap
, HashMap
and LinkedHashMap
. Each of the maps’ four test methods tests the performance using the different enum sizes of 3, 5, 10 and 20 respectively.
Adding a StringBuilder
with CSV generating formatting then made it easy to tabulate the results of each test execution. I created a spreadsheet for the analysis and then executed 10 test runs for each of the following values of READ_CYCLES
: 1, 3, 5, 10 and 20. There were therefore 50 test runs in total.
Results
Here are the results of the analysis, with times given in milliseconds:

READ_CYCLES
== 1
READ_CYCLES
== 3
READ_CYCLES
== 5
READ_CYCLES
== 10
READ_CYCLES
== 20Discussion
In nearly every case, EnumMap
shows a performance improvement when compared to the two hash maps. Similarly, in most cases, HashMap
shows a performance improvement when compared to LinkedHashMap
.
You may have noticed that when the read cycles are low and the number of enum values is also low, the performance difference is less noticeable. Indeed, if read cycles is 1 and enum value count is 3 then HashMap
has a marginal performance advantage when compared to EnumMap
! This is because there is a slightly higher overhead when it comes to creating an EnumMap
relative to creating a HashMap
, and if sufficient use is not made of the read efficiencies of the EnumMap
, the creation overhead predominates.
However, in most cases it would still be advisable to use EnumMap
whenever possible, since this is an edge case and the performance difference is minimal. The knowledge shared by using EnumMap
whenever possible is arguably worth a tiny performance drop in such situations.
I noticed that for 20 read cycles, there appears to be an anomalous result for enums with 5 values for HashMap
when compared to LinkedHashMap
, recording averages of 1118.4ms and 908.2ms respectively. I do not intend to investigate this as it is outside the scope of this article, but it is worth noting as it may be of interest to someone if it is a real effect and not simply a random chance outlier.
Conclusion
In conclusion, when you want to use a Map
with enums as keys, you should probably use an EnumMap
!
However, if iteration order is important to you, and for some reason you wanted to iterate in a different order from the order in which the enum values are declared (otherwise known as their ‘ordinal’) then you should use a LinkedHashMap
. In such a case you might also consider whether you could simply re-order the values in the enum’s definition to make use of the EnumMap
here too.