December 15, 2010

Complex Sorting with Java Comparator

« Documenting Design Patterns with Annotations | Main | jQuery Ajax Tutorial and Examples »

Sometimes developers have to realize more complex sorting algorithms. For example you have a list of customers. First you want to sort by age and second by surname. After that you want to sort by forename.

Comparator Implementation:

import java.util.Comparator;
 
public class CustomerComparator implements Comparator<Customer> {
 
  @Override
  public int compare(Customer c1, Customer c2) {
 
    if (c1.getAge() == c2.getAge()) {
      if (c1.getSurname().compareTo(c2.getSurname()) == 0) {
          return c1.getForename().compareTo(c2.getForename()) {
      } else {
          return c1.getSurname().compareTo(c2.getSurname());
      }
    } else if (c1.getAge() > b2.getAge()) {
        return -1;
    } else {
        return 1;
    }
  }
}

Comparator JUnit Test:

@Test
public void testCustomerComparator() {
    Customer customerFirst = new Customer(44,"Alabama", "Christonson");
    Customer customerSecond = new Customer(23, "Anna", "Sobek");
    Customer customerThird = new Customer(23, "Rafael", "Sobek");

    List<Customer> list = new ArrayList<Customer>();
    list.add(customerThird);
    list.add(customerSecond);
    list.add(customerFirst);
     
    //unsorted test
    Assert.assertEquals(itemThird, unsortedList.get(0));
    Assert.assertEquals(itemSecond, unsortedList.get(1));
    Assert.assertEquals(itemFirst, unsortedList.get(2));

   Comparator<Customer> customerComparator = new CustomerComparator();
   Collections.sort(list, customerComparator);

   //sorted test
   Assert.assertEquals(customerFirst, list.get(0));
   Assert.assertEquals(customerSecond, list.get(1));
   Assert.assertEquals(customerThird, list.get(2));
}

Regards
Rafael Sobek

Technorati Tags:

Posted by rafael.sobek at 8:33 AM in Java

 

[Trackback URL for this entry]

Comment: Vatar at Fr, 17 Dez 12:08 AM

Instead of
if (c1.getAge() > b2.getAge()) {
return -1;
} else {
return 1;

------
return b2.getAge() - c1.getAge();

Comment: nikos lianeris at Fr, 17 Dez 4:07 PM

nice implementation of Comparator interface.Good job

Comment: JJ at Mo, 20 Dez 9:07 AM

None of the code examples compile as is.

first example row 1 should end with semicolon instead of }

first example row 14 b2 should be c2

second example row 13-15, itemXXX should be customerXXX and unsortedList should be just list

Comment: Cpt. Obvious at Di, 21 Dez 3:56 PM

Quite obvious, isn't it? How does this triviality qualify for a blog post?

Comment: Matthew Sandoz at Mi, 22 Dez 9:00 PM

you can also use the apache commons comparator utils like this:


private final Comparator updateTimeComparator = ComparatorUtils.reversedComparator(new BeanComparator("lastUpdated", ComparatorUtils.nullHighComparator(ComparatorUtils.naturalComparator())));

and then chain them like this:

private final Comparator latestDefaultProfileFMDSComparator = ComparatorUtils.chainedComparator(new Comparator[]{
sourcePrecedenceComparator,
updateTimeComparator,
insertTimeComparator,
defaultProfileContactComparator
});

when you want to sort on multiple fields. For large datasets, the beancomparator may have a performance issue but it makes the code easy to generalize and reuse.

Comment: Jason Sperske at Mi, 29 Dez 6:24 PM

public int compare(Customer c1, Customer c2) {
if (c1.getAge() == c2.getAge()) {
int diff = c1.getSurname().compareTo(c2.getSurname());
if (diff == 0) {
return c1.getForename().compareTo(c2.getForename()) {
} else {
return diff;
}
} else {
return c1.getAge() - b2.getAge();
}
}

Now if you use a stable sort (which Collections.sort is)
then you can string these together to create a sort priority

public class CustomerAgeComparator implements Comparator<Customer> {
@Override
public int compare(Customer c1, Customer c2) {
return c1.getAge() - b2.getAge();
}
}

public class CustomerForenameComparator implements Comparator<Customer> {
@Override
public int compare(Customer c1, Customer c2) {
return c1.getForename().compareTo(c2.getForename());
}
}

public class CustomerSurnameComparator implements Comparator<Customer> {
@Override
public int compare(Customer c1, Customer c2) {
return c1.getSurname().compareTo(c2.getSurname());
}
}

Then you would call sort in order of increasing priority.

Collections.sort(list, new CustomerForenameComparator());
Collections.sort(list, new CustomerSurnameComparator());
Collections.sort(list, new CustomerAgeComparator());

These three calls will preform more comparisons but over reasonably sized Lists (under 10,000 for modern processors) you would be hard pressed to even measure the difference, and what you lose in terms of speed and brevity you make up for in terms of flexibilty.

Comment: codesmuggler at So, 30 Jan 9:38 PM

In my opinion - you can't say that this is "more complex sorting algorithm". "Compare" operation is only thing that is more complex than usual, not the algorithm.
As an addition I can say that Collections.sort() uses merge sort combined with insertion sort on standard Oracle's implementation.

Your comment:

(not displayed)
 
 
 

Live Comment Preview: