sebastiandaschner blog


Java 8 Comparator

monday, august 11, 2014

In Java 8 there is a really handy new feature regarding creation of comparators.
Instead of writing large compareTo methods in the Comparable class, one can simply define different comparators on demand (more like SQL ORDER BY statements than a single definition of sorting carved in stone).

For example given the following POJO:

public class Task {

    private String name;
    private Integer priority; // can be null
    private boolean finished;
    private Date updated; // can be null

    // Getters & Setters ...
}

Assuming you want to order by not-finished tasks first, then by highest prioritization, then by last updates and finally by name.

In SQL this would be ORDER BY finished ASC, priority DESC, updated DESC, name ASC.

If you let your Task implement Comparable<Task> here this would look like:

@Override
public int compareTo(final Task that) {
    if (that.finished ^ this.finished) {
        return (this.finished) ? 1 : -1;
    }

    if (that.priority != null && this.priority != null && !that.priority.equals(this.priority)) {
        return that.priority.compareTo(this.priority);
    } else if (that.priority != null || this.priority != null) {
        return (that.priority != null) ? 1 : -1;
    }

    if (that.updated != null && this.updated != null && !that.updated.equals(this.updated)) {
        return that.updated.compareTo(this.updated);
    } else if (that.updated != null || this.updated != null) {
        return (that.updated != null) ? 1 : -1;
    }
    return this.name.compareTo(that.name);
}

Since Java 8, you can also define new Comparators Builder-pattern like:

Comparator
    .comparing(Task::isFinished)
    .thenComparing(Comparator
        .comparing(Task::getPriority, Comparator.nullsFirst(Integer::compareTo)).reversed())
    .thenComparing(Comparator
        .comparing(Task::getUpdated, Comparator.nullsFirst(Date::compareTo)).reversed())
    .thenComparing(Task::getName);

The several order definitions are chained and contain lambda expressions (or just method references as you can see).

Please keep in mind that null values are not checked and will result in a nice NPE. To overcome this, nullsFirst() or nullsLast() can be used additionally.

You may notice that the reversed()-sortings are realized in a separate Comparator definition.
The point is that the method reverses the whole comparator which is defined so far and not just the last order by definition. So just calling Comparator.comparing(Task::isFinished)
.thenComparing(Task::getPriority, …​nulls…​).reversed() would reverse the finished field as well.

 

Found the post useful? Subscribe to my newsletter for more free content, tips and tricks on IT & Java: