This article introduces EnumMap and EnumSet, which is a collection class under the java.util package.

The Map and Set structures are especially used in our daily work, and often used to store data or pass parameters, but there is no way to control the data setting of Map, we have no idea what kind of data others will put into it, or if the type and number of data Key of our Map is fixed in some scenarios, then in this case How can we improve the security and performance of the system?

This time we can consider using EnumMap, EnumMap is a Map, and secondly its key can only be an enumeration, we all know that the number of instances in the enumeration is fixed, and still pre-compiled, so to a large extent to ensure the security of the data, but also to improve the performance of certain.

EnumMap

Let’s look at how to use EnumMap, first we need to create an enumeration Color

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.javaisland.demo.enums;

public enum Color {
  BLUE("blue", "color is blue"),
  RED("red", "color is red"),
  ;
  public String color;
  public String desc;

  Color(String color, String desc) {
    this.color = color;
    this.desc = desc;
  }
}

After creating a test class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.javaisland.demo.enums;

import java.util.EnumMap;

public class ColorTest {

  public static void main(String[] args) {
    EnumMap<Color, String> enumMap = new EnumMap<>(Color.class);
    enumMap.put(Color.RED, "red");
    enumMap.put(Color.BLUE, "blue");
    System.out.println(enumMap.get(Color.BLUE));
  }
}

We can see that we need to pass in an enum class when constructing EnumMap, and the subsequent put and get are the same as Map, except that this time the key must be the enum instance when put is used. Next, let’s look at how the put and get methods of EnumMap are implemented, and looking at the JDK source code we can see that

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    public V put(K key, V value) {
        typeCheck(key);

        int index = key.ordinal();
        Object oldValue = vals[index];
        vals[index] = maskNull(value);
        if (oldValue == null)
            size++;
        return unmaskNull(oldValue);
    }

When put is done, type checking is done first and if the passed in enum is not an enum or not the enum specified during construction, an exception will be thrown here. When the type check is passed, the index of the enumeration instance is obtained by the ordinal() method of the enumeration, which returns an int value, and the returned value is related to the order of the enumeration when it is written. RED.ordinal()will return 1. Once the index is obtained, thevalue` value will be placed in the corresponding array position.

It’s easy to get the data, just get the index directly from key and then remove the data from the array.

1
2
3
4
    public V get(Object key) {
        return (isValidKey(key) ?
                unmaskNull(vals[(Enum<? >)key).ordinal()]) : null);
    }

You can see that the efficiency of put and get of the whole EnumMap is very high, both are directly directed to process based on index in a one-dimensional array. So you can try to use this approach to improve performance in similar scenarios in the future.

EnumSet

Let’s take a look at EnumSet, EnumSet is a collection used to manipulate Enum, an abstract class that has two inherited classes, JumboEnumSet and RegularEnumSet. To use it, you need to determine the enumeration type. An empty EnumSet can be created and used later by the following way.

1
2
3
4
5
6
  public static void main(String[] args) {
    EnumSet<Color> enumSet = EnumSet.noneOf(Color.class);
    enumSet.add(Color.BLUE);
    enumSet.add(Color.RED);
    System.out.println(enumSet.size());
  }

The construction of EnumSet is a bit more involved, we can create empty sets, and we can also create a complete set directly, there is no need to create an empty one and then do the add operation, as follows

1
2
3
4
  public static void main(String[] args) {
    EnumSet<Color> enumSet = EnumSet.allOf(Color.class);
    System.out.println(enumSet.size());
  }

In addition, as mentioned earlier, the ordinal() method of enumeration is used, so when constructing an EnumSet we can also construct only all the enumeration values between the specified two enumeration ranges, and note here that the enumeration of the second argument of the range method cannot precede the first enumeration.

1
EnumSet.range(Color.BLUE,Color.RED);

You can also use the of method of EnumSet to construct the specified enumeration set. We can find out from the source code that no matter what method is used to construct it, the bottom is to construct an empty set first and then add the corresponding enumeration elements. The logic for constructing an empty set is as follows. Here we can see that when the number of enumerations is greater than 64, the subclass JumboEnumSet is used, otherwise it is the subclass RegularEnumSet.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<? >[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }

The other methods are the same as for a regular Set, and you can use the set in your work exactly as it is.

Summary

Today we introduced you to two uncommon, but very useful JDK set classes that can still improve performance in some scenarios.