Chitika

Thursday, September 23, 2004

Comparator and Comparable

Again, the topic on Collection usage seems overlooked by most Java developers. Sorting is something that's very common in any programming world, therefore we shouldn't bother to reinvent the wheel for such purpose. As common as it is, yet not many of us know how to do it correctly and efficiently.

In the real business world, there are many cases where we're required to perform sorting on a Collection of some user-defined objects. If the element of the Collection is a String type, or any other class which implements Comparable, then the sorting can be performed in the natural order. But sometimes, we want to perform a different criteria for sorting, or probably we want to sort a user-defined type.

Here's where Comparator comes into play. Clean and extensible.

Here's an example of a simplified user-defined type:

public class Customer {
  private String firstName;
  private String middleName;
  private String lastName;

  // setter & getter methods..
}

In the above class, the most commonly found as natural ordering would be order by lastName. This can easily be implemented using the Comparable interface, i.e.

public class Customer implements Comparable {
  // as shown previously..

  public int compareTo (Object o) {
    if (o == null || !(o instanceof Customer)) {
      throw new IllegalArgumentException ("...");
    }
    Customer c = (Customer) o;
    String cLastName = c.getLastName();

    if (lastName == null && cLastName == null) return 0;
    // assuming you want null values shown last
    if (lastName != null && cLastName == null) return -1;
    if (lastName == null && cLastName != null) return 1;
    return lastName.compareTo (cLastName);
  }
}

Sorting a List of Customer objects would be as simple as:

  Collections.sort (customerList);

But, if we want to use a different ordering, e.g. order by the first name, then we cannot use the natural ordering as defined within the Customer class. Instead, we have to define an alternative ordering, in the form of a Comparator class.

public class CustomerFirstNameComparator
implements Comparator {
  // use singleton whenever possible..

  public int compare (Object o1, Object o2) {
    if (o1 == null && o2 == null) return 0;
    // assuming you want null values shown last
    if (o1 != null && o2 == null) return -1;
    if (o1 == null && o2 != null) return 1;
    if (!(o1 instanceof Customer) ||
        !(o2 instanceof Customer)) {
      throw new IllegalArgumentException ("...");
    }

    Customer c1 = (Customer) o1;
    Customer c2 = (Customer) o2;
    String firstName1 = c1.getFirstName();
    String firstName2 = c2.getFirstName();

    if (firstName1 == null && firstName2 == null) return 0;
    // assuming you want null values shown last
    if (firstName1 != null && firstName2 == null) return -1;
    if (firstName1 == null && firstName2 != null) return 1;
    return firstName1.compareTo (firstName2);
  }
}

Sorting a List of Customer objects by their first name, would be:

  // assuming you implement singleton..
  Comparator comparator =
    CustomerFirstNameComparator.getInstance();

  Collections.sort (customerList, comparator);

Simple, clean & extensible. You can start defining more and more Comparator classes to suit your needs. As it is a Java class, you can also perform complex comparison on the objects.

12 comments:

  1. I was searching to get a better understanding for comparator vs comparable. And my search ends here only.

    ReplyDelete
  2. Good one!
    The explanation for the Comparator is really perfect...
    Thanks.

    ReplyDelete
  3. I also looked for a good article, following is also another good one that helped me a lot. Link

    ReplyDelete
  4. Very very nicely and with good examples explained..

    ReplyDelete
  5. "to sort a user-defined type. " this is what really helped.
    i was wondering if you could develop a little the idea of using singleton..why here? or where is it useful, what situations.
    thank you

    ReplyDelete
  6. Exelent solution! One cuestion though... if we use a comparator, the class Costumer needs to implement the comparable interface, although we are using a custom comparation method?

    ReplyDelete
  7. yes it is good example. But it show only very simple kind of comparison like in Api doc. Are you also able to write article how fullfill really complex comparison? e.g. According to
    1.lastName(dsc),
    2. firstName(only smith and only wesson),
    3. middleName(that contains charachter z)?

    ReplyDelete
  8. good stuff man, by the way while using comparable interface in Java and overriding compareTo method its worth noting that compareTo must be compatible with s equals method in Java i.e. if two objects are equal via equals method compareTo method must return "0" for them, failing this may result in some subtle bug when you store those objects in collection class like Arraylist in Java.

    Source: How to use Comparator and Comparable in Java

    ReplyDelete