Java Tips Weblog

  • Blog Stats

    • 2,571,556 hits
  • Categories

  • Archives

Bean Comparator

Posted by Rob Camick on October 23, 2008

Java provides basic support for sorting Beans stored in Lists and Arrays by using the Collections.sort(…) and Arrays.sort(…) methods respectively. If you want your Bean to be sortable, then your Bean must implement the Comparable interface. This will provide your Bean with a “Natural Order” sort. What do you do when you want to sort your Bean on a different property? Well, in this case you will need to create a custom Comparator to be used by the sort methods. But, do we really need to create custom Comparators for each sortable property, or is there an easier way?

Welcome to the world of reflection. Using reflection we can create a BeanComparator that will allow us to sort on any property that has a zero parameter method that returns the value of the property. In other words methods like getFirstName(), getAge(). The BeanComparator will then do a natural order sort on that property. For getFirstName(), a sort on the String will be done. For getAge(), a sort on the Integer will be done. In this case, the reflection API will return the wrapper class of the primitive “int” type.

Using the BeanComparator you will also be able to set a few properties to control the sort:

  • ascending (the default) or descending
  • ignore case (the default) or use case. This property is only used when sorting Strings
  • nulls last (the default) or nulls first

A simple example? Lets assume you have a Person class with the some properties accessed by the following methods:

  • getFirstName()
  • getLastName()
  • getAge()

To sort a group of people by age you could do the following:

BeanComparator bc = new BeanComparator(Person.class, "getAge");
Collections.sort(people, bc);

The BeanComparator can also be used with the GroupComparator (discussed in an earlier post) to provide sorting on multiple properties at the same time.

It should be noted that by using reflection you will take a bit of a performance hit. Using a collection of 10k people the BeanComparator based sort took about 400ms, whereas the natural order sort took about 16ms. In this case you may want to create a custom Comparator to improve performance. A custom Comparator for our sample Person class with a getAge() method might look like:

public class PersonAgeComparator implements Comparator<Person>
{
    public int compare(Person p1, Person p2)
    {
        return p1.getAge() - p2.getAge();
    }
}

To invoke the sort you would use:

Collections.sort(people, new PersonAgeComparator());

As you can see it is not a lot of work to create the custom Comparator.

Finally, as mentioned earlier, you can also implement a “natural order” sort on the age property. The basic code added to the Person class would be:

class Person implements Comparable<Person>
{
    ...

    public int compareTo(Person person)
    {
        return getAge() - person.getAge();
    }
}

To Invoke the sort you would use:

Collections.sort(people);

You can only have a single natural sort order but you can use as many custom Comparators or BeanComparators as you wish.

Get The Code

BeanComparator.java

See Also

Group Comparator

Related Reading

Java Tutorials: Object Ordering
Java API: java.lang.Comparable
Java API: java.util.Comparator

9 Responses to “Bean Comparator”

  1. Keith Corlett said

    Brilliant!

    Just a couple of ideas…

    1. Could you reflect –> generate & compile a Comparator on the fly thus eliminating the O(n) reflections overhead? I’m going to work it right now, and (given my current state of inebriiiation) I should have something compiling by Tuesday next.

    2. Howsaboutta BeanFilter in the same vein?

  2. camickr said

    Thanks for the comment. Both are good suggestions. I’ll keep them at the back of my mind for future entries (if I can figure out how to do it).

    I currently have a backlog of working code I want to post first.

  3. I decided to play a bit with ASM today, and put a simple dynamic comparator at
    http://www.tincancamera.com/examples/java/asm/src/com/tincancamera/asm/beans/

    The benchmarks show it a very little bit faster than a hard-coded Comparator at sorting (probably as the generics-safe one has an extra call through its synthetic compare(Object,Object) method to the compare(T,T) ).

  4. camickr said

    Interesting, the ASM package must be very efficient at generating the byte code. Although I must admit I don’t understand a line of the code.

    Pete’s simple comparator doesn’t support the sorting options presented here, but its nice to know that you will get the bang for your buck if you spend the time to create a more generic comparator with ASM.

  5. Frederik said

    Ever looked at the class org.apache.commons.beanutils.BeanComparator

    • Rob Camick said

      I wrote the class to learn about reflection. Then I did find the above class, which confirmed my approach was reasonable. A couple of differences. This class:

      a) provides a couple of sort properties, which I believe would have to be implemented as custom Comparators otherwise

      b) requires you to specify the class of the Bean you are comparing. This allows me to save the Method used to get the bean data for each comparison when the class is created, instead of using reflection again every time to find the method. As a BeanComparator is relatively slow, I figure every little bit helps.

  6. wchargin said

    Very clever idea. Thanks.

    Regarding performance, would it be faster to use reflection to change the visibility of the underlying field to public and then read it? Or would SecurityManagers nuke that? Or is it just a Very Bad Idea?

  7. It’s awesome designed for me to have a website, which is good in support of my knowledge. thanks admin

Leave a comment