6 Advanced Comparator and Comparable Examples in Java 8

The JDK 8 release has completely changed the way you compare objects and sort them in Java. The new features of the Java 8 language e.g. lambda expression and method reference have made it easier to implement both Comparator and Comparable interface, as you don't need an Anonymous class for inline implementation. Now, you can create Comparators in just one line by using lambdas and method reference as we'll see in this article. Other features like providing default and static methods on interfaces have also made a huge difference when it comes to Comparator. They helped Java API designers to redesign and evolve existing interfaces, which wasn't possible earlier without breaking existing clients of those interfaces.

The JDK 8 release has added many useful changes to the classes you use on your day-to-day programming like List, Map, Collection, Iterable, and Comparator. The java.util.Comparator is one of the luckiest classes in JDK 8, it has got a host of new powerful methods, which has completely changed the way you compare objects.

For example, by using the comparing() method it's easier to compare objects on any particular field and by using the thenComparing() method you can easily chain multiple comparators to provide a more realistic but complex ordering.

It also provides several utility methods to reverse the order of comparator, sort objects on natural order, and making a comparator null safe by ordering null either and first or the last position using nullsFirst() and nullsLast() methods.

Most of these methods work well with lambdas and method reference which eventually lead to a cleaner code while sorting a list or array of objects in Java 8.  You can further join The Complete Java Masterclass to learn more about the modern style of coding in Java.




6 ways to Sort List of Objects with Multiple Fields using Comparator and Comparable in Java 8

Here is my list of selected examples for comparing objects in Java 8. These examples not only includes the new way of writing comparators using lambdas and method reference but also how to leverage new Comparator methods like comparing(), thenComapring(), reversed(), naturalOrder(), nullsFirst(), etc to provide sophisticated ordering and sorting in Java 8.

The object we'll sort

In order to demonstrate the use of Comparator and Comparable in Java 8, we need a domain object. I like books, so I'll create a Book object with some fields to demonstrate how you can sort a list of book objects by single or multiple fields using Java 8 features

Here is how our Book object will be like:

class Book implements Comparable<Book> {
  private String title;
  private String author;
  private int price;
 
  public Book(String title, String author, int price) {
    this.title = title;
    this.author = author;
    this.price = price;
  }
 
  public String getTitle() {
    return title;
  }
 
  public String getAuthor() {
    return author;
  }
 
  public int getPrice() {
    return price;
  }
 
  public void setTitle(String title) {
    this.title = title;
  }
 
  public void setAuthor(String author) {
    this.author = author;
  }
 
  public void setPrice(int price) {
    this.price = price;
  }
 
  @Override
  public String toString() {
    return "Book [title=" + title + ", author=" + author + ", price=" + price
        + "]";
  }
 
  // the old way to implement CompareTo method to compare
  // object by multiple fields, you'll learn new way as well
  @Override
  public int compareTo(Book b) {
    int i = this.title.compareTo(b.title);
    if (i != 0)
      return i;
 
    i = this.author.compareTo(b.author);
    if (i != 0)
      return i;
 
    return Integer.compare(this.price, b.price);
  }
 
}

and here is the list of Books which we'll sort in this article:

List<Book> listOfBooks = new ArrayList<>();
listOfBooks.add(new Book("Effective Java", "Joshua Bloch", 32));
listOfBooks.add(new Book("Java Puzzlers", "Joshua Bloch", 22));
listOfBooks.add(new Book("Java Concurrency in Practice", "Brian Goetz", 42));
listOfBooks.add(new Book("Java SE 8 for Really Impatient", "Cay S. Horstmann", 34));
listOfBooks.add(new Book("Core Java", "Cay S. Horstmann",32));
 
Now, let's see how we can sort this list of objects using new features of Java 8 and new methods added to the Comparator class.



1. Writing Comparator using Lambda Expression

While learning Java 8, the first thing a Java developer should learn is to implement SAM interfaces using lambda expressions. Since Comparator and Comparable are also SAM interfaces e.g. they contain just one abstract method like compare() and compareTo(), you can easily implement them using a lambda expression.

For example, if you want to write a Comparator to sort Books by their author, you can write like in the following example:

Comparator<Book> byAuthor = (b1, b2) -> b1.getAuthor().compareTo(b2.getAuthor());

just compare this to the old Java SE 7 way of writing comparator in Java:

 Comparator<Book> byAuthorOldWay = new Comparator<Book>() {
      public int compare(Book b1, Book b2) {
        return b1.getAuthor().compareTo(b2.getAuthor());
      }
    };

You can see, in Java 8, you can write a Comparator using lambda expression in Just one line. Now, you can sort the list of books by using this comparator either by using the Collections.sort() method or the newly added List.sort() method, which sorts the list in place, as shown below:

listOfBooks.sort(byAuthor);
System.out.println("list of books after sorting: " + listOfBooks);
 

This will print the following output:

list of books after sorting: [
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34], 
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Effective Java, author=Joshua Bloch, price=32], 
Book [title=Java Puzzlers, author=Joshua Bloch, price=22]
]
 
 
You can see that Brian Goetz's book top the list because "B" comes first in lexicographic order, followed by Cay. S. Horstmann's book and finally Josh Bloch's books.

So, you have now learned how to create a comparator using lambda expression in Java 8 in just one line, simple and easy right? but Wait... Things will get even simpler when we'll start using method references in the next example. Btw, if you want to learn more about the lambda expression of Java 8 then you should also see the What's New in Java 8 course on Pluralsight.


Top 5 Examples of Comparator and Comparable in Java 8



2. Writing Comparator using Method Reference

When you are simply accessing a property of an object in lambda expression then you can replace the reference to lambdas by method reference. This results in more cleaner and readable code than you ever have seen in Java. For example, we can write the Comparator of the previous example using method reference as shown below:

Comparator<Book> byAuthor = Comparator.comparing(Book::getAuthor);

This way, it's very easy to create different Comparator for sorting into different parameters e.g. let's create a Comparator that sorts byTitle and byPrice as well:

Comparator<Book> byTitle = Comparator.comparing(Book::getTitle);
Comparator<Book> byPrice = Comparator.comparing(Book::getPrice);

When it comes to sorting, you can also directly pass the Comparator to Collections.sort() or List.sort() method as shown in the following example:

listOfBooks.sort(Comparator.comparing(Book::getPrice)); 
System.out.println("list of books after sorting by price: " + listOfBooks);
 
Output
list of books after sorting by price: [
Book [title=Java Puzzlers, author=Joshua Bloch, price=22], 
Book [title=Effective Java, author=Joshua Bloch, price=32], 
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34], 
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42]
]


3. Chaining Comparators to compare multiple fields of Objects

If you are amazed to see the previous example of Comparator, you will be even more surprised to see this example, which demonstrates another magic of Java 8 which allows you to chain methods to perform sophisticated comparison logic.

One of the common requirements in the real world is to compare objects by multiple fields like the first compare by the author and if the author is same the as compare by title or price.

You can use thenComparing() method to chain multiple Comparators in Java 8 to compare objects by multiple fields like comparing a list of a person by name and by age or comparing a list of books by author and price as shown in the following example:

Comparator<Book> byAuthorThenByPrice = Comparator.comparing(Book::getAuthor)
                                                 .thenComparing(Book::getPrice);

You can then use this comparator to sort a list of objects by multiple fields as shown below:

listOfBooks.sort(Comparator.comparing(Book::getAuthor)
                           .thenComparing(Book::getPrice));
System.out.println("sorting list of books by multiple fields,
                          by author and then by price: "
                                + listOfBooks);
 
Output:
Sorting list of books by multiple fields, by author and then by price: [
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42], 
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34], 
Book [title=Java Puzzlers, author=Joshua Bloch, price=22], 
Book [title=Effective Java, author=Joshua Bloch, price=32]
]

You can see that the list of the book is first sorted by author and that's why Brian Goetz's book comes first and then the next two books are from Cay S. Horstmann, where the book with less price comes first like  Core Java comes before Java SE 8 for Really Impatient, one of my favorite books to learn Java 8.

Sorting Objects using Comparator and Comparable in Java 8


4. Sorting List of objects in reverse order of Comparator

The combination of comparing() method and method reference offers several possibilities. One of them is to create a reverse order comparator. The JDK 8 API also helps as it provides a new reversed() method in the Comparator class which returns a comparator that imposes the reverse ordering of this comparator.

You can use this method to compare objects in the reverse order of any Comparator as shown in the following example:

listOfBooks.sort(Comparator.comparing(Book::getAuthor).reversed());

Here we are sorting a list of books in reverse order of author i.e. it will print Book object in the descending order of author as seen below:

Sorting list in reverse order of authors: [
Book [title=Effective Java, author=Joshua Bloch, price=32], 
Book [title=Java Puzzlers, author=Joshua Bloch, price=22], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34], 
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42]
]
  
You can see that Joshua Bloch's Effective Java comes first now and Brian Goetz's Java Concurrency in Practice comes last. If you haven't read them yet then they should be on your reading list for the coming weekend.


5. Sorting objects by the natural order

It's easy to compare objects by their natural order by using JDK 8's Comparator.naturalOrder() method. It provides the same ordering provided by the Comparable interface. You can pass this comparator to the Collections.sort() or List.sort() method to sort a list of objects by their natural order.

If you look at the code of Book class, you will see that the compareTo() method first compares books by title, and if the title is the same then by author, and if the author is the same then by price. Let's see an example of sorting objects on natural order using Comparator.naturalOrder()

listOfBooks.sort(Comparator.naturalOrder());
System.out.println("sorting list of books in their natural order using 
                      Comparator.naturalOrder(): " + listOfBooks); 
 
Output:
sorting list of books in their natural order using Comparator.naturalOrder(): [
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Effective Java, author=Joshua Bloch, price=32], 
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42], 
Book [title=Java Puzzlers, author=Joshua Bloch, price=22], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34]
]

You can see That Core Java by Cay S. Horstmann comes first while Java SE 8 for Really Impatient by the same author comes last.

Btw, you can also sort a list in natural order by using Comparable and you can do so by not passing any Comparator to List.sort() method i.e. just passing null as shown below:

Sorting list of books in their natural order using Comparable: [
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Effective Java, author=Joshua Bloch, price=32], 
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42], 
Book [title=Java Puzzlers, author=Joshua Bloch, price=22], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34]
]
 
 
You can see that both outputs are the same. Btw, I prefer the first approach to the second because passing null to sort method doesn't look clean.

I am surprised why didn't Java API designer overloaded sort() method just like Collection.sort() were the sort() which doesn't accept any parameter sort in the natural order.

Maybe, they are just concern with the number of methods introduced in the List class.
Btw, if you want to deep dive into new methods added on the existing interface like List and Map in Java 8 then The Complete Java Masterclass is a good resource.

7. Null-safe comparing using nullsFirst() and nullsLast() Comparator



6. Null-safe Sorting using nullsFirst() and nullsLast() Comparator

One of the interesting addition to the Comparator interface in JDK 8 is the null-safe comparators. You can now convert your non-null-safe comparator to a null-safe Comparator by using either nullsFirst() or nullsLast() methods. The nullsFirst() method returns a null-friendly comparator that considers null to be less than non-null.

When both are null, they are considered equal. If both are non-null, the specified Comparator is used to determine the order. If the specified comparator is null, then the returned comparator considers all non-null values to be equal.

On the other hand, the nullsLast() method considers null to be greater than non-null, hence they come last in the ascending order of objects. Let's see an example of nullsFirst() and nullsLast() method of Java 8 using Comparator.

In order to demonstrate null safe sorting in Java 8, we first need to add a null element in the list of books, as soon as you do this your code will break with NullPointerexception because our comparator implementations are not handling nulls. If you run the code given in the first example, you will see the following exception:

Exception in thread "main" java.lang.NullPointerException
at java.util.ComparableTimSort.binarySort(ComparableTimSort.java:262)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:189)
at java.util.Arrays.sort(Arrays.java:1312)
at java.util.Arrays.sort(Arrays.java:1506)
at java.util.ArrayList.sort(ArrayList.java:1454)
at Main.main(Main.java:25)
 
In order to solve this exception and sort a list that may contain null elements, we'll use the nullsFirst() method as shown below:

Comparator<Book> byAuthor = (b1, b2) -> b1.getAuthor().compareTo(b2.getAuthor()); 
listOfBooks.sort(Comparator.nullsFirst(byAuthor)); 
System.out.println("sorting list of books with nulls first " + listOfBooks);
 
Output
sorting list of books with nulls first [
null, 
Book [title=Java Concurrency in Practice, author=Brian Goetz, price=42], 
Book [title=Java SE 8 for Really Impatient, author=Cay S. Horstmann, price=34], 
Book [title=Core Java, author=Cay S. Horstmann, price=32], 
Book [title=Effective Java, author=Joshua Bloch, price=32], 
Book [title=Java Puzzlers, author=Joshua Bloch, price=22]
]
 

You can see that NullPointerException has gone away and null has come first in the sorted order. If you use the nullsLast() method then null will come last in the sorting order. The big benefit of using this method is that now you can easily sort a list of objects without worrying about nulls. Your Comparator is also free from any null pointer handling logic.


That's all about some of the essential Comparator and Comparable examples in Java 8. You can see that JDK 8 has really made a Comparator class more useful and with the help of lambda expression and method reference, it becomes super easy to provide a Comparator implementation on the fly. 

You don't need to write boilerplate code which comes with Anonymous inner class, both lambdas and method reference allow you to write clean code for comparing and sorting objects in Java 8.

There are several things that have become easier in Java e.g. comparing objects by multiple parameters. Earlier, you used to write several lines of code to implement ordering on multiple fields but now it has become easier due to comparing() and thenComparing() methods of the Java 8 Comparator class, which allows you to compose a complex ordering by chaining multiple comparators.

Last, but not least, you can now safely handle nulls in the list while sorting. By using nullsFirst() or nullsLast() comparator you can put null either at first or last position while sorting a list of objects containing null elements. 

If you are eager to learn more about new enhancements made on other key Java classes like Map or List, I suggest you read Java SE 8 for Really Impatient. It includes a nice summary of miscellaneous changes of JDK8 which are useful in your day-to-day coding.


Related Java 8 Tutorials
If you are interested in learning more about the new features of Java 8, here are my earlier articles covering some of the important concepts of Java 8
  • How to use Stream class in Java 8 (tutorial)
  • Difference between abstract class and interface in Java 8? (answer)
  • 5 Books to Learn Java 8 from Scratch (books)
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • 5 Free Courses to learn Java 8 and 9 (courses)
  • How to sort the map by keys in Java 8? (example)
  • How to convert List to Map in Java 8 (solution)
  • What is the default method in Java 8? (example)
  • How to format/parse the date with LocalDateTime in Java 8? (tutorial)
  • How to use peek() method in Java 8 (example)
  • How to use filter() method in Java 8 (tutorial)
  • How to sort the may by values in Java 8? (example)
  • How to join String in Java 8 (example)
  • 7 Best Collections and Stream Courses for Java developers (courses)

Thanks for reading this article so far. If you like this tutorial then please share it with your friends and colleagues. If you have any questions, doubts or feedback about this tutorial and my explanation then please drop a comment.

P. S. - If you are looking for some free courses to learn recent changes on Java 8 and Java 9 then you can also see this list of Free Java 8 and 9 Courses for Programmers.

5 comments:

  1. Replies
    1. Thanks Anonymous, glad you find this comparator examples useful.

      Delete
  2. Can you add a section to sort objects on multiple fields where one field is sorted on reverse order? For example, you need to sort Employee object on name and then age on reverse order, which means oldest first

    ReplyDelete
  3. I might have missed it... but what to do not if the book object is null but the author is null. I.e. if an instance variable being compared is null. I am still getting null exceptions.

    ReplyDelete

Feel free to comment, ask questions if you have any doubt.