Comparator with Java Lambda Expression Examples

Learn how to use Java Lambda Expression based Comparator to easily sort collections in forward and reverse direction.

Overview

In order to sort a collection, the elements of the collections are required to be compared by default. When the collection elements belong to Java predefined data types, we do not need to provide the comparison logic. On the other hand, when we have a collection of a custom object, we have to provide the comparison strategy.

One of the way to do that, is to create a Comparator implementation for our object and write the comparison logic in the compare() method. In this tutorial we will learn How we can use Java Lambda Expressions to sort collections by providing inline comparator implementations.

Prepare a Collection

Let’s first prepare a Collection that we will sort throughout this tutorial. Assume our collection holds records of student objects, where the Student object has only a few fields.

public class Student { private final Long studentId; private final String firstName; private final String lastName; private final Integer age; // Constructor, Getter, and Setter }
Code language: Java (java)

Now, we will create a few dummy student records and put them in a simple ArrayList instance.

Student student1 = new Student(2L, "Karl", "F", 18); Student student2 = new Student(3L, "Jack", "P", 20); Student student3 = new Student(5L, "Nick", "G", 17); Student student4 = new Student(1L, "Tom", "F", 21); Student student5 = new Student(4L, "Jon", "W", 22); students = new ArrayList<>(); students.add(student1); students.add(student2); students.add(student3); students.add(student4); students.add(student5);
Code language: Java (java)

Note that, the records in the list are in random order.

Sort Without Lambda Expressions

The basic way to sort a List elements is to use sort() method. However, if the list contains custom objects – like ours, we have to provide a comparator instance that can compare between the instances of custom object.

Next is an example of using Sorting a collection using Anonymous inner class or inline implementation of Comparator.

students.sort(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getFirstName().compareTo(o2.getFirstName()); } });
Code language: Java (java)

The inline implementation of Comparator interface compares two Student objects based on the first name field.

Student(studentId=3, firstName=Jack, lastName=P, age=20)
Student(studentId=4, firstName=Jon, lastName=W, age=22)
Student(studentId=2, firstName=Karl, lastName=F, age=18)
Student(studentId=5, firstName=Nick, lastName=G, age=17)
Student(studentId=1, firstName=Tom, lastName=F, age=21)

The output shows, our ArrayList is sorted based on the first name field of Student.

Sort Using Basic Lambda Expression

Java Lambda Expressions, help reducing a lot of builder plate code blocks and makes the code concise. As the Comparator is a Java Functional Interface, we can use a lambda expression instead of passing an inline implementation class.

students.sort( (o1, o2) -> o1.getFirstName().compareTo(o2.getFirstName()));
Code language: Java (java)

Here, we have provided a lambda expression for the compare() method of Comparator. We can see the code is now much cleaner.

Sort Using Method Reference

In the previous example we have provided an inline implementation of comparator in the form of Java lambda expression. However, instead of putting the sorting logic inline, we can put that in a reusable method and use method reference to sort a collections based on that logic.

For example, let’s create a Student Sort Utility class.

public class StudentSortUtils { public static int comparingFirstName( Student student1, Student student2) { return student1.getFirstName() .compareTo(student2.getFirstName()); } public static int comparingFirstAndLastName( Student student1, Student student2) { return (student1.getFirstName().equals(student2.getFirstName())) ? comparingFirstName(student1, student2) : student1.getLastName().compareTo(student2.getLastName()); } }
Code language: Java (java)

Here, we have two versions of the compare methods. Both of them accept the same arguments and returns the same type as that of Comparator#compare() method. However, they have a different comparison strategies.

Now, we can sort a collection using Method Reference, like this.

students .sort(StudentSortUtils::comparingFirstName);
Code language: Java (java)

Alternatively, we can also sort based on more multiple fields, using the respective method reference.

<meta charset="utf-8">students .sort(StudentSortUtils::comparingFirstAndLastName);
Code language: Java (java)

Sort Using Comparator Factory Method

The Java Comparator interface has a static factory method of comparing(). The comparing() method accepts a key extractor function and builds a Comparator instance dynamically that compares the given key.

For example, if we want to sort students like based on the age, we can create a Comparator using its static factory method comparing() like this:

Comparator.comparing(student -> student.getAge())
Code language: Java (java)

However, Java Lambda expressions allow us to replace the lambda expression with a direct method reference.

students.sort(Comparator.comparing(Student::getAge));
Code language: Java (java)

Sorting Based on Multiple Fields

When we want to sort a collection based on multiple fields, we can simply can create composition of multiple conditional expressions. For example, in order to sort the student collection based on the last name and first name, we will check if last names are same, compare with first names; else compare based on the last names.

And, our lambda expression will look like this.

(o1, o2) -> { if (o1.getLastName().equals(o2.getLastName())) { return o1.getFirstName().compareTo(o2.getFirstName()); } else { return o1.getLastName().compareTo(o2.getLastName()); } });
Code language: Java (java)

Alternatively, the Comparator supports compositing multiple Comparator instances together. Using that we can sort a collection with multiple fields.

students.sort( Comparator.comparing(Student::getLastName) .thenComparing(Student::getFirstName) );
Code language: Java (java)

Here, we used Comparator#comparing() factory method to create a last name based comparator and used thenComparaing() – another factory method that compares based on the first name. Both of these comparators will be logically composed in a single Comparator instance.

Student(studentId=1, firstName=Tom, lastName=F, age=21)
Student(studentId=5, firstName=Nick, lastName=G, age=17)
Student(studentId=3, firstName=Jack, lastName=P, age=20)
Student(studentId=4, firstName=Jon, lastName=W, age=22)

Thus, our output shows, that list of the Student instances is now sorted based on two different fields.

Sort using Reverse Sorting (Descending Order)

So far, we sorted the list of students based on various fields in ascending order. In this section, we will discuss about sorting fields in reverse or descending order.

Reverse Sort with Lambda Expression

When we use lambda expressions for Comparators, we provide our own comparison logic. In order to sort the fields in the descending order, we simply need to reverse the lambda expression.

Example, of sorting in the descending order of the first name of the student.

students.sort( (o1, o2) -> o2.getFirstName().compareTo(o1.getFirstName()));
Code language: Java (java)

Here, we are comparing first name field of the second instance to that of the first instance.

Student(studentId=1, firstName=Tom, lastName=F, age=21)
Student(studentId=5, firstName=Nick, lastName=G, age=17)
Student(studentId=2, firstName=Karl, lastName=F, age=18)
Student(studentId=4, firstName=Jon, lastName=W, age=22)
Student(studentId=3, firstName=Jack, lastName=P, age=20)

That is why, we get reversed sorted output.

Reverse Sort using Comparator.reverseOrder()

Alternatively, if we are using Comparator‘s static factory methods, we can use a static method of Comparator#reverseOrder() to instruct the reverse sorting order.

students.sort(Comparator.comparing( Student::getAge, Comparator.reverseOrder()));
Code language: Java (java)

Note that, we have provided an additional parameter to instruct the reverse sorting.

Student(studentId=4, firstName=Jon, lastName=W, age=22)
Student(studentId=1, firstName=Tom, lastName=F, age=21)
Student(studentId=3, firstName=Jack, lastName=P, age=20)
Student(studentId=2, firstName=Karl, lastName=F, age=18)
Student(studentId=5, firstName=Nick, lastName=G, age=17)

Hence in the output, we get the oldest student on the top.

Sort with Comparator and Mixed Sorting Order

In addition to this, we can use reverse sorting along with Comparator compositions to make even more complex sorting expressions. Like, we want to sort collections in an ascending order of a field and descending order of the other.

students.sort(Comparator.comparing( Student::getLastName) .thenComparing(Student::getAge, Comparator.reverseOrder()) );
Code language: Java (java)

Let’s print the list after sorting.

Student(studentId=1, firstName=Tom, lastName=F, age=21)
Student(studentId=2, firstName=Karl, lastName=F, age=18)
Student(studentId=5, firstName=Nick, lastName=G, age=17)
Student(studentId=3, firstName=Jack, lastName=P, age=20)
Student(studentId=4, firstName=Jon, lastName=W, age=22)

We can see, our Comparator compositions with mixed sorting order has produced intended output.

Summary

This was an overview of using Java Lambda based Comparator expressions to sort collections. We started with an example of sorting a collection without lambda expression, where we had to provide an inline anonymous implementation of Comparator interface.

Next we understood, how using Lambda expressions for the same sort operation makes the code concise. We also covered using Comparator interface’s static factory methods. The factory methods creates a Comparator instance based on the given fields and conditions.

Then we wrote some complex comparison operations like sorting based on multiple fields of a collection. Lastly, we touched upon doing reverse sort with lambda expressions as well as Comparator instance and also covered implementing a mixed sort order.

For full source of the examples used here, please visit our Github Repository.