Composite comparators in Java

Some time ago a fellow developer wrote a really comprehensive blog post (unfortunately only available in german) about comparator implementations in Java. More specifically it is about composite comparators used to compare entities according to different attributes. The best solution for Java 7 he comes up with is a comparator

class FoobarComparator implements Comparator {
  @Override
  public int compare(Foobar lhs, Foobar rhs) {
    return compoundCompare(
      lhs.getLastName().compareTo(rhs.getLastName()),
      lhs.getFirstName().compareTo(rhs.getFirstName()),
      lhs.getPlaceOfBirth().compareTo(rhs.getPlaceOfBirth()),
      lhs.getDateOfBirth().compareTo(rhs.getDateOfBirth()));
  }
}

with a reusable compoundCompare()-method

// utility method used with static import
int compoundCompare(int... results) {
  for (int result : results) {
    if (result != 0) {
      return result;
    }
  }
  return 0;
}

While this solution is quite clean and a vast improvement over the critized implementations it has the flaw that it eagerly evaluates all attributes even though short-circuiting may be possible for many entities. This may lead to performance problems in some cases. So he goes on to explain how Java 8 will fix this problem with Lambdas or another solution he calls “KeyMethodComparator”.

Now I want to show you an implementation very similar to his approach above but without the performance penalty and possible in Java 7 using the composite pattern:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

class FoobarComparator implements Comparator<Foobar> {

  private List<Comparator<Foobar>> defaultFoobarComparison =
    Arrays.<Comparator<Foobar>>asList(
      new Comparator<Foobar>() {
        @Override
        public int compare(Foobar lhs, Foobar rhs) {
          return lhs.getLastName().compareTo(rhs.getLastName());
        }
      },
      new Comparator<Foobar>() {
        @Override
        public int compare(Foobar lhs, Foobar rhs) {
          return lhs.getFirstName().compareTo(rhs.getFirstName());
        }
      },
      new Comparator<Foobar>() {
        @Override
        public int compare(Foobar lhs, Foobar rhs) {
          return lhs.getPlaceOfBirth().compareTo(rhs.getPlaceOfBirth());
        }
      },
      new Comparator<Foobar>() {
        @Override
        public int compare(Foobar lhs, Foobar rhs) {
          return lhs.getDateOfBirth().compareTo(rhs.getDateOfBirth());
        }
      });

  @Override
  public int compare(Foobar lhs, Foobar rhs) {
    for (Comparator<Foobar> comp : defaultFoobarComparison) {
      int result = comp.compare(lhs, rhs);
      if (result != 0) {
        return result;
      }
    }
    return 0;
  }
}

It features the lazy evaluation demanded by my fellow for performance and allows flexible construction of different composite comparators if you, e.g. add a constructor accepting a list of comparators.
Imho, it is a quite elegant solution using standard object-oriented programming in Java today and not only in the future.

About these ads

3 Responses to Composite comparators in Java

  1. Frisian says:

    Hmm, why not make it shorter, like so?
    public class GenericCompositeComparator implements Comparator{
    Comparator[] comparators;

    public GenericCompositeComparator(Comparator… comparators) {
    this.comparators = comparators;
    }

    @Override
    public int compare(V lhs, V rhs) {
    for (Comparator comp : comparators) {
    int result = comp.compare(lhs, rhs);
    if (result != 0) {
    return result;
    }
    }
    return 0;
    }
    }

    and use it like so:
    public class FoobarComparator extends GenericCompositeComparator {
    @SuppressWarnings(“unchecked”)
    public FoobarComparator() {
    super(
    new Comparator() {
    @Override
    public int compare(Foobar lhs, Foobar rhs) {
    return lhs.getLastName().compareTo(rhs.getLastName());
    }
    },
    new Comparator() {
    @Override
    public int compare(Foobar lhs, Foobar rhs) {
    return lhs.getFirstName().compareTo(rhs.getFirstName());
    }
    });
    }
    }

    Now imagine that with lamda expressions or method references…

    • Miq says:

      Yes, you are right. Your refactoring takes the whole approach one step further resulting in a generic and reusable CompositeComparator. I wanted to stay as close as possible to the original code in the mentioned blog.
      Lambdas will reduce noise dramatically in such cases so I am really looking forward to them….

  2. Frisian says:

    Oops, these classes were supposed to use generics, but the types were stripped during posting.
    The class was supposed to use a type T and the constructor to accept comparators of ? super T

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 64 other followers

%d bloggers like this: