Complete Guide to Comparators in Spring Framework with examples.

1. Introduction

In the previous article we discussed Comparator interface and 17 of its methods. Before you dive into this article I would recommend reading the previous article. In this article we will look at the Comparators class of Spring. The Comparators class is in package org.springframework.util.comparator and part of spring-core-5.2.5-RELEASE dependency. 

We will also see how we can use Comparator of Java 8 in conjunction with Spring Comparator.

2. Content

The Comparators class is a public abstract class which consists of 5 methods. Below are the methods that we will discuss with examples.

public static <T> Comparator<T> comparable()

public static <T> Comparator<T> nullsLow()

public static <T> Comparator<T> nullsLow(Comparator<T> comparator)

public static <T> Comparator<T> nullsHigh()

public static <T> Comparator<T> nullsHigh(Comparator<T> comparator)

3. public static <T> Comparator<T> comparable()

comparable() method’s responsibility is to compare based on natural ordering which will be defined in Comparable interface. The comparable() method expects that the type T implements the Comparable interface. If the Comparable interface is not implemented by the class then usage of this method will throw runtime exception called ClassCastException. I would highly recommend not to use this method as it is unsafe. Instead of using this method I would recommend to use java.util.Comparator.naturalOrder(). Why? Because it gives you compile time error if the objects are not Comparable.

Example : Sort the List<Transaction>

List<Transaction> transactions = Transactions.getDataSet();

Comparator<Transaction> comparableComparator 
        = Comparators.comparable();

transactions.sort(comparableComparator);

The sort method will print below stacktrack.

java.lang.ClassCastException: com.justamonad.tutorials.common.Transaction cannot be cast to java.lang.Comparable
	at org.springframework.util.comparator.ComparableComparator.compare(ComparableComparator.java:31)
	at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
	at java.util.TimSort.sort(TimSort.java:220)
	at java.util.Arrays.sort(Arrays.java:1512)
	at java.util.ArrayList.sort(ArrayList.java:1454)
	at com.justamonad.tutorials.comparator.SpringComparatorsTest.test(SpringComparatorsTest.java:20)

If the objects are Comparable then the sorting would work without any exceptions. Below is the example where we sort numbers.

Comparator<Integer> comparable = Comparators.comparable();

List<Integer> numbers = Arrays.asList(56, 73, 42, 3, 7);

numbers.sort(comparable);

// expected output
List<Integer> output = Arrays.asList(3, 7, 42, 56, 73);

Assert.assertEquals(output, numbers);

4. public static <T> Comparator<T> nullsLow()

nullsLow() method is similar to nullsFirst() method of Comparator interface. But again this method will throw ClassCastException at runtime if the objects to be sorted are not Comparable. It would be safer to use Comparator.nullsFirst() method as it will give compile time error if objects are not Comparable.

Example : Sort List<Integer> and push all null elements to the beginning of the List<>.

List<Integer> numbers = Arrays.asList(56, 73, null, 42, 3, 7, null);

numbers.sort(Comparator.nullsFirst(Integer::compare));

// expected output
List<Integer> output = Arrays.asList(null, null, 3, 7, 42, 56, 73);

Assert.assertEquals(output, numbers);

5. public static <T> Comparator<T>
nullsLow(Comparator<T> comparator)

nullLow(Comparator) method accepts a Comparator and hence it is safer to use this method than nullLow(). 

Example :  Give a List<Transaction> sort it by date. Push null elements to the beginning of the List<>.

List<Transaction> transactions = Transactions.getDataSet();

transactions.add(1, null); // add new element at index 1 as null.

Comparator<Transaction> comparator 
        = (t1, t2) -> t1.date().compareTo(t2.date());

Comparator<Transaction> nullsLow = Comparators.nullsLow(comparator);

transactions.sort(nullsLow);
Output
Before sorting
2020-06-19, US
null
2020-06-15, US
2020-06-19, CA
2020-06-24, AU

After sorting
null
2020-06-15, US
2020-06-19, US
2020-06-19, CA
2020-06-24, AU

6. public static <T> Comparator<T> nullsHigh()

nullsHigh() method is similar to nullsLast() method of Comparator interface. But again this method will throw ClassCastException at the runtime if the objects to be sorted are not Comparable. It would be safer to use Comparator.nullsLast() method as it will give compile time error if objects are not Comparable.

Example : Sort List<Integer> and push null elements to the end of the List<>.

List<Integer> numbers = Arrays.asList(56, 73, null, 42, 3, 7, null);

numbers.sort(Comparator.nullsHigh(Integer::compare));

// expected output
List<Integer> output = Arrays.asList( 3, 7, 42, 56, 73, null, null);

Assert.assertEquals(output, numbers);

7. public static <T> Comparator<T>
nullsHigh(Comparator<T> comparator)

nullsHigh(Comparator) method accepts a Comparator and hence it is safer to use this method than nullsHigh().

Example :  Give a List<Transaction> sort it by date and push null elements to the end of the List<>.

List<Transaction> transactions = Transactions.getDataSet();

transactions.add(1, null);

Comparator<Transaction> comparator 
    = (t1, t2) -> t1.date().compareTo(t2.date());

Comparator<Transaction> nullsHigh 
    = Comparators.nullsHigh(comparator);

transactions.sort(nullsHigh);
Output
Before sorting
2020-06-19, US
null
2020-06-15, US
2020-06-19, CA
2020-06-24, AU

After sorting
2020-06-15, US
2020-06-19, US
2020-06-19, CA
2020-06-24, AU
null

9. Using Comparator interface with Spring Comparators

Spring’s Comparators returns instance of Comparator so we can access Comparator’s methods directly.

Example : Give a List<Transaction> reverse sort it by date and push null elements to the end of the List<>.

List<Transaction> transactions = Transactions.getDataSet();

transactions.add(1, null);

Comparator<Transaction> comparator 
    = (t1, t2) -> t1.date().compareTo(t2.date());

Comparator<Transaction> nullsLow 
    = Comparators.nullsLow(comparator);

Comparator<Transaction> reversed = nullsLow.reversed();

transactions.sort(reversed);
Before sorting
2020-06-20, US
null
2020-06-16, US
2020-06-20, CA
2020-06-25, AU

After sorting by date and nulls low
null
2020-06-16, US
2020-06-20, US
2020-06-20, CA
2020-06-25, AU

After reversing the previous result
2020-06-25, AU
2020-06-20, US
2020-06-20, CA
2020-06-16, US
null

8. Conclusion

Spring Comparators are useful but the problem of throwing runtime exception of ClassCastException is a deal breaker. It is prudent to use methods from the Comparator interface rather than use Spring Comparators class. You can read about it here. I use Spring Framework a lot and have written a few articles on it like implementing bridge pattern using ApplicationContextAware, writing unit tests cases and validators in Spring. 

Code can be found on Github. Click here.

Leave a Reply

Your email address will not be published. Required fields are marked *