Search This Blog

Saturday, 9 November 2013

A Set of Enums

Why would I need a collection Of Enums? Consider that we do have a simple enum:
public enum Rating {
   TOP, GOOD, PASSABLE, BAD, POOR, FAIL
}
Now if I want to get all the values I would call the Rating.values method. This would give me an array of Enums. Any reason why I would want a set of the same ?

One scenario I can think of is when we want to work with a generic set of enums - say code that simply displays a set. We couldn't do that cleanly with the values method as it returns arrays of a type. But then I could cast it to an object array and still pull it of
public static void display(Object[] enums) {
      System.out.print("values are ");
      for (Object object : enums) {
         System.out.print(object + ", ");
      }
      System.out.println(" Size : " + enums.length);
   }
lets just say I want to work with sets of enums. Maybe I need the additional power of the Collections API. The way to do this would be something like:
  Set<Rating> ratingSet = new HashSet<Rating>(Rating.values().length);
  Collections.addAll(ratingSet, Rating.values());
Since enums are similar to a grouping of compile time constants, the JRE has created certain collections optimized for used with enums. One of these is the EnumSet. (The above ramblings were an attempt to come up with a use case for these collections. Sigh.) From the class comments:
Enum sets are represented internally as bit vectors. This representation 
is extremely compact and efficient. The space and time performance of 
this class should be good enough to allow its use as a high-quality, 
typesafe alternative to traditional int-based "bit flags".
Consider the add method of HashSet:
public boolean add(E e) {
  return map.put(e, PRESENT) == null;
}
This implementation adds the entries to a map. But now consider the same method from RegularEnumSet:
public boolean add(E e) {
      typeCheck(e);
      long oldElements = elements;
      elements |= (1L << ((Enum) e).ordinal());
      return elements != oldElements;
   }
As seen above there is no attempt to add the enum to the set. Instead a certain bit value is updated.  
The RegularEnumSet works on the concepts of bit manipulation where a single bit in a 64 bit variable ( a long value) indicates if a particular enum exists in the set or not. So remove() or add() or removeAll() or contains() are all reduced to very fast bit manipulations. As we are working with 64 bits, the RegularEnumSet can obviously only be used for enums with a most 64 values. For larger enums we have the JumboEnumSet. Now that we know its advantages lets us look at some of the API available in EnumSet.
public static void main(String[] args) {
      // EnumSet is abstract.
      System.out.println("All available values are : " + EnumSet.allOf(Rating.class));
      EnumSet<Rating> ratings = EnumSet.noneOf(Rating.class);
      System.out.println("Is bad added ? " + ratings.contains(Rating.BAD));
      ratings.add(Rating.BAD);
      System.out.println("Is bad added ? " + ratings.contains(Rating.BAD));
      System.out.println("A set of values except BAD " + EnumSet.complementOf(ratings));
      System.out.println("A set of Positive values : " + EnumSet.range(Rating.TOP, Rating.PASSABLE));
      System.out.println("A set of select values : " + EnumSet.of(Rating.TOP, Rating.PASSABLE));
   }
The output is as below:
All available values are : [TOP, GOOD, PASSABLE, BAD, POOR, FAIL]
Is bad added ? false
Is bad added ? true
A set of values except BAD [TOP, GOOD, PASSABLE, POOR, FAIL]
A set of Positive values : [TOP, GOOD, PASSABLE]
A set of select values : [TOP, PASSABLE]
As seen, The EnumSet class provides a series of static methods to create the EnumSet. Its package scoped constructor is as below:
EnumSet(Class<E>elementType, Enum[] universe) {
//..code
}
As seen the class specifically accepts the Enum type and also an array of all enum values. So do not try to mix Enum values Also the iterator that we receive from above set will include in which the enum constants in the order they are declared.

No comments:

Post a Comment