High-performance collections EnumMap and EnumSet
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
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
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
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, the
value` 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.
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.
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
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.
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
.
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.