Saturday, 7 June 2014

How to use EnumSet in Java with Example

EnumSet is one of the specialized implementation of Set interface for enumeration type, introduced in Java 1.5 along with 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 few years ago, until I come across one of the finest book for Java programmers, Effective Java. It has an Item on EnumSet, which highlight some typical use-cases for this collection class instead of using int variable and bitwise operator. Since Enum constants are unique and has pre-defined length, as you can not define a new enum constant at runtime; it allows Java API designer to highly optimize EnumSet. If you look closely, EnumSet also teaches you 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 method to create 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. Second good thing is there are two concrete sub-classes of EnumSet e.g. RegularEnumSet and JumboEnumSet, but both are package-private, which means client can not use them directly. Only way to use them is via EnumSet. At runtime, depending upon key size of requested Enum, implementation automatically decide which implementation to use. This provides you immense flexibility and control to rollout a new better version of EnumSet implementation without any change on client side. Anyway, this article is not just about design lessons form this class but more importantly how and when to use EnumSet in Java program. I have shared simple example to demonstrate power of EnumSet, which you will see in next section.



EnumSet Example in Java

How to use EnumSet in Java with ExampleIn this example, we have used basic colours RED, GREEN and BLUE to create 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 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 single enum type. Adding 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 implementation available depending upon size of key universe. RegularEnumSet is chosen if number of instances is less than 64, otherwise JumboEnumSet is used.

3) As described in Effective Java, EnumSet can be safely used as 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) EnumSet iterator is weekly consistent and never throws ConcurrentModificationException, and may or may not show effect of any modification to the Set, while 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 package. I really like how they shielded implementation class RegularEnumSet and JumboEnumSet and provides factory methods to create EnumSet. This way intelligence of choosing implementation remains with library itself, by making both of these classes package-private, there is no worry of client directly accessing them. You can use EnumSet to pass arguments to methods and to create meaningful groups from enum.

No comments:

Post a Comment