Search This Blog

Loading...

Wednesday, 1 May 2013

Sorting enums in java

Consider the below code:
I have defined a simple enum:
enum Rank {
 RANK1, RANK2, RANK3
}
Next is the main method.Here I have added the enum entries to a TreeSet instance.
public class TestEnum {

 public static void main(String[] args) {
  TreeSet<Rank> ranks = new TreeSet<Rank>();
  ranks.addAll(Arrays.asList(Rank.values()));
  for (Rank rank : ranks) {
   System.out.println(rank);
  }
 }

}
I would have expected the code to crash at runtime with an exception that goes:
Exception in thread "main" java.lang.ClassCastException:Rank cannot be cast 
to java.lang.Comparable
But on running the same:
RANK1
RANK2
RANK3
This wasn't what I was expecting. For I did not provide any Comparable implementation. TreeSet expects the items to implement comparable so as to successfully sort the items. I looked into the source code of the Enum class.
/**
* This is the common base class of all Java language enumeration types.
*
*/
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
Enum's base class implements Comparable. So all enums created have the compareTo method as a part of its API.
        /**
  * 
  * Compares this enum with the specified object for order. Returns a
  * negative integer, zero, or a positive integer as this object is less
  * than, equal to, or greater than the specified object.
  * 
  * Enum constants are only comparable to other enum constants of the
  * same enum type. The natural order implemented by this
  * method is the order in which the constants are declared.
  */

 public final int compareTo(E o) {
  Enum other = (Enum) o;
  Enum self = this;
  if (self.getClass() != other.getClass() && // optimization
    self.getDeclaringClass() != other.getDeclaringClass())
   throw new ClassCastException();
  return self.ordinal - other.ordinal;
 }
The code compares the ordinal value of the enum instances. Now what is this ordinal property?
The ordinal is one of the only two instance variable found in the enum class:
private final int ordinal;
private final String name;
Based on the documentation the ordinal of an enumeration constant is its position in its enum declaration, where the initial constant is assigned an ordinal of zero.
Accordingly I modified the for loop slightly:
for (Rank rank : ranks) {
 System.out.println(rank + " " + rank.ordinal());
}
The outputs is:
RANK1 0
RANK2 1
RANK3 2
As seen the ordinal is equivalent to the position of each enum in the declaration. So with all enums we have a guaranteed sorting technique available.

The problem with this approach is that if we have some logic that depends on the sort sequence of our enums, than that code will break every time somebody changes the sequence of elements in the enum or adds/removes an entry.
In such cases it is best to override the method - That however will fail at compilation !! The compareTo method that we saw earlier is marked final. So the only option here is to provide a custom comparator. Here I have modified the Rank class to have a rank property that is used to decide the sort order
enum Rank {
 RANK1(3), RANK2(2), RANK3(1);
 
 private final int rank;

 private Rank(int rank) {
  this.rank = rank;
 }

 public int getRank() {
  return rank;
 }
}
The custom Comparator uses the same property too:
class RankComparator implements Comparator<Rank> {

 @Override
 public int compare(Rank o1, Rank o2) {
  return o1.getRank() - o2.getRank();
 }
}
On running the test code:
public class TestEnum {

 public static void main(String[] args) {
  final List<Rank> rankList = Arrays.asList(Rank.values());
  
  TreeSet<Rank> ranks = new TreeSet<Rank>();
  ranks.addAll(rankList);

  System.out.println("--- Using the default sorting ---");
  for (Rank rank : ranks) {
   System.out.println(rank);
  }

  ranks = new TreeSet<Rank>(new RankComparator());
  ranks.addAll(rankList);
  
  System.out.println("--- Using the business rules based sorting ---");
  for (Rank rank : ranks) {
   System.out.println(rank);
  }
 }
}
The output is as below:
--- Using the default sorting ---
RANK1
RANK2
RANK3
--- Using the business rules based sorting ---
RANK3
RANK2
RANK1
As seen the two results generated a different sort order. Using our custom comparator in code will ensure that any modifications in the enum declarations does not break our sorting mechanism.

10 comments:

  1. There is little need for anyone to use TreeSet. The EnumSet class advertises:

    The iterator returned by the iterator method traverses the elements in their natural order (the order in which the enum constants are declared). The returned iterator is weakly consistent: it will never throw ConcurrentModificationException and it may or may not show the effects of any modifications to the set that occur while the iteration is in progress.

    Only if you want your own custom Comparator do you need a TreeSet.

    ReplyDelete
  2. Hi Eric,
    The concern is not that sorting isn't working correctly. Its that the sorting is dependent on position of enum constants in the code.
    When working with several developers, there is always the possibility that somebody else might add to, modify the enum constants thereby affecting all code dependent on the sort process.
    I think if your code does use enum based sorts, than it is better to use the custom Comparator.
    Ya, but good to know about the EnumSet class.

    ReplyDelete
  3. HI,
    Nice piece of information shared . I am planning to go for online training on java so i watched lots of videos on youtube and also searched and came across http://www.wiziq.com/course/12145-the-6-week-complete-java-primer-with-training-certificate i don't know weather this is going to work for me or not . So wanted to ask has anybody studied from this site? I am really confused should i go for online training or not. Other guidance will be very helpful .....

    ReplyDelete
  4. Hi Alisha,
    Thanks. No idea about the online training though. Never done any.

    ReplyDelete
  5. Hi alisha,
    I have no idea about this online java training course you are talking about but i have taken many other courses from http://www.wiziq.com which has worked for me . It is really cool stuff which they provide like videos,notes,cd's and also the teachers are also good. So even i was planning to learn java so now when i came across ur comment even i have enrolled in http://www.wiziq.com/course/12145-the-6-week-complete-java-primer-with-training-certificate because i have studied from this site before also so i have faith on this one...

    ReplyDelete
  6. The information which you have provided is very good and easily understood.
    It is very useful who is looking for java Online Training.

    ReplyDelete
  7. This is the information that I was looking for and let me tell you one thing that is it is very useful for who is looking for Java Online Training.

    ReplyDelete
  8. Hi, probably our entry may be off topic but anyways, I have been surfing around your blog and it looks very professional. It’s obvious you know your topic and you

    appear fervent about it. I’m developing a fresh blog plus I’m struggling to make it look good, as well as offer the best quality content. I have learned much at your

    web site and also I anticipate alot more articles and will be coming back soon. Thanks you.


    Java Training in Chennai

    ReplyDelete
  9. Nice Information regardinging Java.. I was really impressed by seeing your article about Java Online Training.. It was owesome..

    ReplyDelete
  10. This is nice blog and unique information related to JAVA. Thanks for sharing such information...

    J2EE Training in Chennai

    ReplyDelete