EnumSet is one of the specialized implementations of the Set interface for an enumeration type, introduced in Java 1.5 along with the enumeration type itself. Programmer often stores Enum into common collection classes e.g. HashSet or ArrayList, mostly because they are unaware of this little gem. Even I wasn't aware of this class a few years ago until I come across one of the finest books for Java programmers, Effective Java. It has an Item on EnumSet, which highlights some typical use-cases for this collection class instead of using int variable and bitwise operator. Since Enum constants are unique and have pre-defined lengths, as you can not define a new enum constant at runtime; it allows Java API designers to highly optimize EnumSet.
If you look closely, EnumSet also teaches you a couple of good design practices to create flexible and maintainable code. For example, EnumSet is an abstract class, and it provides lots of static factory methods to create an instance of EnumSet e.g. EnumSet.of(...). This method takes advantage of method overloading and variable argument to provide Set of only provided Enum constants.
The second good thing is there are two concrete subclasses of EnumSet e.g. RegularEnumSet and JumboEnumSet, but both are package-private, which means a client can not use them directly.
The only way to use them is via EnumSet. At runtime, depending upon the key size of the requested Enum, implementation automatically decide which implementation to use. This provides you immense flexibility and control to roll out a new better version of EnumSet implementation without any change on the client side.
Anyway, this article is not just about design lessons from this class but more importantly how and when to use EnumSet in the Java program. I have shared a simple example to demonstrate the power of EnumSet, which you will see in the next section.
1) EnumSet is a special Set implementation, only applicable for Enums in Java, but you can only store instances of the single enum type. Adding an instance of different enum will result in compile time error, as EnumSet provide type-safety.
2) EnumSet internal representation is extremely efficient and represented as bit vectors. Library itself chooses one of two implementations available depending upon the size of a key universe. RegularEnumSet has chosen if a number of instances are less than 64, otherwise JumboEnumSet is used.
3) As described in Effective Java, EnumSet can be safely used as a type-safe alternative of traditional int based "bit flags". EnumSet provides much-needed readability, without compromising performance.
4) Iterator returned by EnumSet traverse the elements in their natural order, i.e. the order on which enum constants are declared, or the order returned by ordinal() method.
5) An EnumSet iterator is weakly consistent and never throws ConcurrentModificationException, and may or may not show the effect of any modification to the Set while the iteration is in progress.
6) EnumSet is also not synchronized in Java. Though if you need, you can make EnumSet synchronized similar to other collection by using utility methods from Collections class. Here is how to do that :
Set<YourEnum> s = Collections.synchronizedSet(EnumSet.noneOf(YourEnum.class));
7) EnumSet is an abstract class, which means you cannot create its instance using new() operator. This is actually carefully thought to provide special implementation, and that's why EnumSet provides several static factory methods for creating instance e.g. noneOf() returns an empty EnumSet with specified enum type, EnumSet.of(....) returns Set of specified enum constants and allOf() method creates an enum set containing all elements of specified enum.
8) EnumSet also provides methods like complementOf(EnumSet es) and copyOf(EnumSet es) to create EnumSet from other EnumSet, coplementOf() returns enum set with containing all the elements of this type that are not contained in the specified set.
That's all about EnumSet in Java. It's one of the useful class, which is perfect for certain use cases, but it also teaches a lot of lesson on class design and use of the package. I really like how they shielded the implementation class RegularEnumSet and JumboEnumSet and provides factory methods to create EnumSet. This way intelligence of choosing implementation remains with the library itself, by making both of these classes package-private, there is no worry of the client directly accessing them. You can use EnumSet to pass arguments to methods and to create meaningful groups from enum.
If you look closely, EnumSet also teaches you a couple of good design practices to create flexible and maintainable code. For example, EnumSet is an abstract class, and it provides lots of static factory methods to create an instance of EnumSet e.g. EnumSet.of(...). This method takes advantage of method overloading and variable argument to provide Set of only provided Enum constants.
The second good thing is there are two concrete subclasses of EnumSet e.g. RegularEnumSet and JumboEnumSet, but both are package-private, which means a client can not use them directly.
The only way to use them is via EnumSet. At runtime, depending upon the key size of the requested Enum, implementation automatically decide which implementation to use. This provides you immense flexibility and control to roll out a new better version of EnumSet implementation without any change on the client side.
Anyway, this article is not just about design lessons from this class but more importantly how and when to use EnumSet in the Java program. I have shared a simple example to demonstrate the power of EnumSet, which you will see in the next section.
EnumSet Example in Java
In this example, we have used basic colours RED, GREEN and BLUE to create a custom colour by using EnumSet. As I said, EnumSet is very good for combining effects, whether it's text styles e.g. BOLD and UNDERLINE, as described in Effective Java, or combining basic colours to create custom color here. If you have MS paint, you can even test the combination of colours e.g. you can combine RED and BLUE to create YELLOW, combine all three to create WHITE, or combining RED and BLUE to create PINK.import java.util.EnumSet; import java.util.Set; /** * Simple Java Program to demonstrate how to use EnumSet. * It has some interesting use cases and it's specialized collection for * Enumeration types. Using Enum with EnumSet will give you far better * performance than using Enum with HashSet, or LinkedHashSet. * * @author Javin Paul */ public class EnumSetDemo { private enum Color { RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255); private int r; private int g; private int b; private Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } public int getR() { return r; } public int getG() { return g; } public int getB() { return b; } } public static void main(String args[]) { // this will draw line in yellow color EnumSet<Color> yellow = EnumSet.of(Color.RED, Color.GREEN); drawLine(yellow); // RED + GREEN + BLUE = WHITE EnumSet<Color> white = EnumSet.of(Color.RED, Color.GREEN, Color.BLUE); drawLine(white); // RED + BLUE = PINK EnumSet<Color> pink = EnumSet.of(Color.RED, Color.BLUE); drawLine(pink); } public static void drawLine(Set<Color> colors) { System.out.println("Requested Colors to draw lines : " + colors); for (Color c : colors) { System.out.println("drawing line in color : " + c); } } } Output: Requested Colors to draw lines : [RED, GREEN] drawing line in color : RED drawing line in color : GREEN Requested Colors to draw lines : [RED, GREEN, BLUE] drawing line in color : RED drawing line in color : GREEN drawing line in color : BLUE Requested Colors to draw lines : [RED, BLUE] drawing line in color : RED drawing line in color : BLUE
Important properties of EnumSet in Java
Like any other Collection class, EnumSet also has some special properties, which help you to decide when to use EnumSet in your Java application. Keep a note of these properties, behaviours:1) EnumSet is a special Set implementation, only applicable for Enums in Java, but you can only store instances of the single enum type. Adding an instance of different enum will result in compile time error, as EnumSet provide type-safety.
2) EnumSet internal representation is extremely efficient and represented as bit vectors. Library itself chooses one of two implementations available depending upon the size of a key universe. RegularEnumSet has chosen if a number of instances are less than 64, otherwise JumboEnumSet is used.
3) As described in Effective Java, EnumSet can be safely used as a type-safe alternative of traditional int based "bit flags". EnumSet provides much-needed readability, without compromising performance.
4) Iterator returned by EnumSet traverse the elements in their natural order, i.e. the order on which enum constants are declared, or the order returned by ordinal() method.
5) An EnumSet iterator is weakly consistent and never throws ConcurrentModificationException, and may or may not show the effect of any modification to the Set while the iteration is in progress.
6) EnumSet is also not synchronized in Java. Though if you need, you can make EnumSet synchronized similar to other collection by using utility methods from Collections class. Here is how to do that :
Set<YourEnum> s = Collections.synchronizedSet(EnumSet.noneOf(YourEnum.class));
7) EnumSet is an abstract class, which means you cannot create its instance using new() operator. This is actually carefully thought to provide special implementation, and that's why EnumSet provides several static factory methods for creating instance e.g. noneOf() returns an empty EnumSet with specified enum type, EnumSet.of(....) returns Set of specified enum constants and allOf() method creates an enum set containing all elements of specified enum.
8) EnumSet also provides methods like complementOf(EnumSet es) and copyOf(EnumSet es) to create EnumSet from other EnumSet, coplementOf() returns enum set with containing all the elements of this type that are not contained in the specified set.
That's all about EnumSet in Java. It's one of the useful class, which is perfect for certain use cases, but it also teaches a lot of lesson on class design and use of the package. I really like how they shielded the implementation class RegularEnumSet and JumboEnumSet and provides factory methods to create EnumSet. This way intelligence of choosing implementation remains with the library itself, by making both of these classes package-private, there is no worry of the client directly accessing them. You can use EnumSet to pass arguments to methods and to create meaningful groups from enum.
private enum Color {
ReplyDeleteRED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255);
Is not valid java syntax.
Yes, it is. A private constructor with 3 parameters builds the enum instance
DeleteNice article.............
ReplyDeletevery good article
ReplyDelete@Manish, glad that you like this article about EnumSet in Java.
ReplyDeletethank u for sharing
ReplyDelete