Using Google Guava’s Ordering API

We’ve been playing a bit more with Google’s Guava library – what a great library! The most recent thing we used it for was to sort out the comparators for our domain objects. Here’s how.

Using Apache IsisJDO Objectstore, it’s good practice to make your classes implement java.lang.Comparable, and use SortedSet for the collections. You can see this in Isis’ quickstart archetype, where the ToDoItem has a recursive relationship to itself:

public class ToDoItem implements Comparable<ToDoItem> {
    ...
    private SortedSet<ToDoItem> dependencies = Sets.newTreeSet();
    ...
}

How best to implement the compareTo method, though? Here’s the original implementation:

    public int compareTo(final ToDoItem other) {
        if (isComplete() && !other.isComplete()) {
            return +1;
        }
        if (!isComplete() && other.isComplete()) {
            return -1;
        }
        if (getDueBy() == null && other.getDueBy() != null) {
            return +1;
        }
        if (getDueBy() != null && other.getDueBy() == null) {
            return -1;
        }
        if (getDueBy() == null && other.getDueBy() == null ||
            getDueBy().equals(this.getDueBy())) {
            return getDescription().compareTo(other.getDescription());
        }
        return getDueBy().compareTo(getDueBy());
    }

Yuk! Basically it says:
* order the not-yet-completed objects before the completed-objects
* where there’s a tie, order by due date (put those without a due by date last)
* where there’s a tie, order by description.

Here’s how to rewrite that using Guava’s Ordering class. First, let’s create some Ordering instances for the scalar types:

public final class Orderings {
    
    public static final Ordering<Boolean> BOOLEAN_NULLS_LAST = 
        Ordering.<Boolean>natural().nullsLast();
    public static final Ordering<LocalDate> LOCAL_DATE_NULLS_LAST = 
        Ordering.<LocalDate>natural().nullsLast();
    public static final Ordering<String> STRING_NULLS_LAST = 
        Ordering.<String>natural().nullsLast();

    private Orderings(){}
}

Now we can rewrite our ToDoItem‘s compareTo() method in a declarative fashion:

public class ToDoItem implements Comparable {

    ...

    public int compareTo(ToDoItem o) {
        return ORDERING_BY_COMPLETE
               .compound(ORDERING_BY_DUE_BY)
               .compound(ORDERING_BY_DESCRIPTION)
               .compare(this, o);
    }

    public static Ordering<ToDoItem> ORDERING_BY_COMPLETE = new Ordering<ToDoItem>(){
        public int compare(ToDoItem p, ToDoItem q) {
            return Orderings.BOOLEAN_NULLS_LAST.compare(p.isComplete(), q.isComplete());
        }
    };

    public static Ordering<ToDoItem> ORDERING_BY_DUE_BY = new Ordering()<ToDoItem>{
        public int compare(ToDoItem p, ToDoItem q) {
            return Orderings.BOOLEAN_NULLS_LAST.compare(p.getDueBy(), q.getDueBy());
        }
    };

    public static Ordering<ToDoItem> ORDERING_BY_DESCRIPTION = new Ordering()<ToDoItem>{
        public int compare(ToDoItem p, ToDoItem q) {
            return Orderings.STRINGS_NULLS_LAST.compare(
              p.getDescription(), q.getDescription());
        }
    };

Now, admittedly, this hardly warrants all that boilerplate for just a single method in a single class; of course not! But what we have here now is a little algebra that we can use to combine across all the domain classes in our domain model. Other domain classes that use a ToDoItem can order themselves using the ToDoItem‘s natural ordering (accessed from Ordering.natural()), or they can create new orderings using the various ToDoItem.ORDERING_BY_xxx orderings.

About these ads

Posted on February 2, 2013, in Uncategorized and tagged , , . Bookmark the permalink. 2 Comments.

  1. Probably simpler is to put it all in one Comparator and use ComparisonChain:

    public int compare(ToDoItem p, ToDoItem q)
    return ComparisonChain.start()
    .compare(p.isComplete(), q.isComplete(), Ordering.natural().nullsLast())
    .compare(p.getDueBy(), q.getDueBy(), Ordering.natural().nullsLast())
    .compare(p.getDescription(), q.getDescription(), Ordering.natural().nullsLast())
    .result();
    }

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 263 other followers