10 minutes read

EnumSet is a specialized implementation of the Set interface that extends AbstractSet and is used with enum types in Java. If you are planning to use enums, then EnumSet is the right choice. You can see the hierarchy of this class in the following diagram:

EnumSet hierarchy

Creating an EnumSet

EnumSet is an abstract class, so we cannot directly create its object. It means that in order to use EnumSet, the implementation provides various factory methods that come in handy. To use EnumSet in a program, we need to write import java.util.EnumSet;.

Consider an enum class BallsColor that contains a const enum and depicts various colors of balls.

enum BallsColor {
        RED, GREEN, BLUE, YELLOW, ORANGE
}

Visually, we can represent it as a box containing all the enum constants as balls of the respective color.

Balls colors image

To create an EnumSet, we can use the static factory methods. It is important to note that all the static factory methods every time return a new EnumSet of the same type.

Now, let's discuss some of the static factory methods in detail.

Static factory methods

  • of()

If we want a new box of the same dimensions with a different identity (in other words, an EnumSet of the same type) with certain color constants, we should use the of() method and pass the required enums as an argument. We can pass any number of arguments.

Notice how the of() method creates a new box of the same dimension (that is, the same type) but with a different identity (because a box with a blue lid is an enum class and the box with a dark blue lid is an EnumSet).

Balls colors

This is what it looks like syntactically:

EnumSet<BallsColor> colors2 = EnumSet.of(BallsColor.GREEN, BallsColor.BLUE);
System.out.println(colors2); // [GREEN, BLUE]

For the rest of this topic, every new "box" will represent a new EnumSet. Also, consider "the same dimension" as the same type as the one specified on the left-hand side.

  • allOf()

If we need a new box with all the enums in the BallsColor class, we can use the allOf() method to create it. Here we pass the enum class as an argument.

Balls colors and color set

This is what it looks like syntactically:

EnumSet<BallsColor> colorsSet = EnumSet.allOf(BallsColor.class);
System.out.println(colorsSet); // [RED, GREEN, BLUE, YELLOW, ORANGE]
  • noneOf()

If we want to have an empty box of the same dimension, we can use the noneOf() method that will essentially create a new empty box for us. Here we pass the enum class as an argument.

Balls colors and colors set image

Syntactically:

EnumSet<BallsColor> colorsSet = EnumSet.noneOf(BallsColor.class);
System.out.println(colorsSet); // []
  • complementOf()

Suppose we already have a box named color2 with just blue and green in it. To have a new box with all the colors except the ones in color2, we can use the complementOf() method.

Balls colors and new colors set

Syntactically:

EnumSet<BallsColor> newColorsSet = EnumSet.complementOf(colors2); // [GREEN, BLUE]
System.out.println(newColorsSet); // [RED, YELLOW, ORANGE]
  • range()

This method takes two parameters: the first enum and the last enum and returns an EnumSet of all the enums between them, including the specified two.

EnumSet<BallsColor> ballsColors = EnumSet.range(BallsColor.RED, BallsColor.YELLOW);
System.out.println(ballsColors); // [RED, GREEN, BLUE, YELLOW]

It is important to note that the type of EnumSet should be the same as that of enum const being passed in various static factory methods. EnumSet implements the Set interface so we can use all its operations/methods.

Here is an example:

enum Status {
    RECEIVED, PROCESSING, CHECKING, DISPATCHED, PAYMENT_COLLECTION
}

EnumSet<Status> statuses = EnumSet.range(Status.RECEIVED, Status.DISPATCHED);
statuses.remove(Status.CHECKING); // true
statuses.remove(Status.DISPATCHED); // true

statuses.add(Status.CHECKING); 

// Notice how Checking Enum is placed at the correct position 
// as in Status class in spite of adding it afterwards
System.out.println(statuses); // [RECEIVED, PROCESSING, CHECKING]
statuses.contains(Status.PROCESSING); // true

There are many other operations in Java that you can use with HashSet or Set in general.

If you pay attention, EnumSet also teaches you a couple of good design practices to create flexible and maintainable code!

RegularEnumSet and JumboEnumSet

Remember, EnumSet is just an abstract class, which means the actual logic and computation are implemented by the subclasses called RegularEnumSet and JumboEnumSet.

Abstract EnumSet class

The difference is that JumboEnumSet is selected when the size of the enum passed is more than 64. RegularEnumSet uses a single long to represent the bit vector. Each bit of the long element represents a value of the enum. The i-th value of the enum will be stored in the i-th bit, so it's quite easy to know whether a value is present or not. Since long is a 64-bit data type, this implementation can store up to 64 elements, whereas JumboEnumSet uses an array of long type elements for storing purposes.

Characteristics of EnumSet

  1. EnumSet is internally represented in the bit vector. It combines the end performance of the bit field with the many advantages of enum types.

  2. Due to the above-mentioned implementation, EnumSet is faster than HashSet (though it is not guaranteed).

  3. EnumSet can not be used to store any other object except enums. At the same time, you cannot store instances of two different enums.

  4. EnumSet doesn't allow null elements.

  5. It is a mutable set. We can make it immutable by using Collections.unmodifiableSet. Remember final keyword cannot be used to create an immutable data structure. You can refer to this StackOverflow question.

Conclusion

As a rule of thumb, if you want to use a set of enums, EnumSet should be your first choice unless stated otherwise. The internal implementation uses bit arithmetic which is very compact and efficient with enums. The EnumSet class has been divided into two package-private classes depending on the number of enums passed. EnumSet provides static factory methods to interact with it. We can create an immutable EnumSet by wrapping it with Collections.unmodifiableSet at the cost of performance. As always you can find out more about EnumSet in the documentation here.

55 learners liked this piece of theory. 2 didn't like it. What about you?
Report a typo