EnumSet

Since Version 5.0  Java has offered the ability to define enums (short for enumerated types). An enum is a type that has a fixed list of possible values, which is specified when the enum is created. In some ways, an enum is similar to the boolean data type, which has true and false as its only possible values. However, boolean is a primitive type, while an enum is not.

The definition of an enum type has the form:


enum enum-type-name { list-of-enum-values }

This definition cannot be inside a subroutine. You can place it outside the main() routine of the program. The enum-type-name can be any simple identifier. This identifier becomes the name of the enum type, in the same way that “boolean” is the name of the boolean type and “String” is the name of the String type. Each value in the list-of-enum-values must be a simple identifier, and the identifiers in the list are separated by commas. For example, here is the definition of an enum type named Season whose values are the names of the four seasons of the year:


enum Season { SPRING, SUMMER, AUTUMN, WINTER }

By convention, enum values are given names that are made up of upper case letters, but that is a style guideline and not a syntax rule. Enum values are not variables. Each value is a constant that always has the same value. In fact, the possible values of an enum type are usually referred to as enum constants.

Suppose that E is an enumerated type. Since E is a class, it is possible to create objects of type TreeSet<E> and HashSet<E>. However, because enums are so simple, trees and hash tables are not the most efficient implementation for sets of enumerated type values. Java provides the class java.util.EnumSet as an alternative way to create such sets.

Sets of enumerated type values are created using static methods in the class EnumSet. For example, if e1, e2, and e3 are values belonging to the enumerated type E, then the method


EnumSet.of( e1, e2, e3 )

creates and returns a set of type EnumSet<E> that contains exactly the elements e1, e2, and e3. The set implements the interface Set<E>, so all the usual set and collection operations are available. The implementation of these operations is very efficient. The implementation uses what is called a bit vector. A bit is a quantity that has only two possible values, zero and one. A set of type EnumSet<E> is represented by a bit vector that contains one bit for each enum constant in the enumerated type E; the bit corresponding to the enum constant e is 1 if e is a member of the set and is 0 if e is not a member of the set. The bit vectors for two sets of type EnumSet<E> can be very easily combined to represent such operations as the union and intersection of two sets.

The bit vector representation is feasible for EnumSets, but not for other sets in Java, because an enumerated type contains only a small finite number of enum constants.

The function EnumSet.of can be used with any positive number of parameters. All the parameters must be values of the same enumerated type. Null values are not allowed. An EnumSet cannot contain the value null – any attempt to add null to an EnumSet will result in a NullPointerException.

There is also a function EnumSet.range(e1,e2) that returns an EnumSet consisting of the enum constants between e1 and e2, inclusive. The ordering of enum constants is the same as the order in which they are listed in the definition of the enum. In EnumSet.range(e1,e2), e1 and e2 must belong to the same enumerated type, and e1 must be less than or equal to e2.

If E is an enum, then EnumSet.allOf(E.class) is a set that contains all values of type EEnumSet.noneOf(E.class) is an empty set, a set of typeEnumSet<E> that contains no elements at all. Note that in EnumSet.allOf(E.class) and EnumSet.noneOf(E.class), the odd-looking parameter represents the enumerated type class itself. If eset is a set of type EnumSet<E>, then EnumSet.complementOf(eset) is a set that contains all the enum constants of E that are not in eset.

As an example, consider a program that keeps schedules of events. The program must keep track of repeating events that happen on specified days of the week. For example, an event might take place only on weekdays, or only on Wednesdays and Fridays. In other words, associated with the event is the set of days of the week on which it takes place. This information can be represented using the enumerated type:


enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }

The days of the week on which an event takes place would then be a value of type EnumSet<Day>. An object of type RepeatingEvent would have an instance variable of type EnumSet<Day> to hold this information. An event that takes place on Wednesdays and Fridays would have the associated set:


EnumSet.of( Day.WEDNESDAY, Day.FRIDAY )

We could define some common sets of Days as


EnumSet<Day> weekday = EnumSet.range( Day.MONDAY, Day.FRIDAY );
EnumSet<Day> weekend = EnumSet.complementOf( weekday );
EnumSet<Day> everyday = EnumSet.allOf( Day.class );

EnumSets are often used to specify sets of “options” that are to be applied during some type of processing. For example, a program that draws characters in fancy fonts might have various options that can be applied. Let’s say that the options are bold, italic, underlined, strikethrough, and boxed. Note that we are assuming that options can be combined in arbitrary ways. For example, you can have italic, boxed, underlined characters. This just means that we need to keep track of a set of options. If the options are represented by the enumerated type:


enum FontOption { BOLD, ITALIC, UNDERLINED, STRIKETHROUGH, BOXED }

then a set of options is represented by a value of type EnumSet<FontOption>. Suppose that options is a variable of this type that represents the set of options that are currently being applied by the program. Then we can do things like:

  • options = EnumSet.noneOf( FontOption.class ) – Turn off all options.
  • options = EnumSet.of( FontOption.BOLD ) – Use bold, with no other options.
  • options.add( FontOption.BOLD ) – Add bold to any options that are already on.
  • options.remove( FontOption.UNDERLINED ) – Turn underlining off (if it’s on).

This is a nice, safe way to work with sets of options. Applications like this are one of the major reasons that enumerated types were introduced.

Next: Maps