What is comparator
java.util.Comparator is an interface in java used for sorting or ordering of collections such as a List, Set, array etc.
A comparator is mostly used to sort a collection of custom or user defined objects which do not possess a natural ordering.
Natural ordering means sorting by alphabetical order, numbers etc.

Suppose you have a list of student objects which needs to be sorted. This list can not be sorted by inbuilt methods since there is no defined sorting criteria.
A list of numbers can be sorted in order of increasing or decreasing order but on what basis should an employee list be sorted.
Comparator is used to specify this criteria on which the objects should be ordered.
compare method
Comparator is a functional interface having only one abstract method named compare() which is used to order objects of a collection.
Signature of compare() method is

int compare(T objectOne, T objectTwo)

where T is a generic type which is of the type of objects that you want to order.

compare() method returns an integer value whose possible values are

  • 0 : If the values compared are equal,
  • Greater than 0(or positive) : If first object is greater than second,
  • Lesser than 0(or negative) : If first object is lesser than second.

How comparator works
java.util.Collections and java.util.Arrays class have sort() methods which are used for sorting a list and array respectively.
Both these classes have an overloaded method which accepts a Comparator as argument, used for sorting list or array of user defined classes as shown below.

Collections.sort(List<T> list, Comparator<? super T> c)

Arrays.sort(T[] a, Comparator<? super T> c)

where T is a generic type.
Note that the type of Comparator should be the same as the type of List or array.

There are two ways to supply the Comparator object to sort() method.
1. As an object of a class that implements java.util.Comparator interface.
This class should contain implementation of compare() method which defines the ordering criteria for the objects of its class.

2. As an anonymous inner class that implements compare() method(explained later).
Comparator example
Suppose there is a list of book objects. Book class is as below.

public class Book {
   private int price;
   private int pages;
   private String title;

   public Book(String title, int price, int pages) {
      this.title = title;
      this.price = price;
      this.pages = pages;
   }

   public int getPrice() {
      return price;
   }

   public void setPrice(int price) {
      this.price = price;
   }

   public int getPages() {
      return pages;
   }

   public void setPages(int pages) {
      this.pages = pages;
   }

   public String getTitle() {
      return title;
   }

   public void setTitle(String title) {
      this.title= title;
   }
 
   public String toString() {
      return "[Title = " + title + ", price = " + price + ", pages = " + pages + "]";
   }

}

Let us create a list of book objects and print each of the book objects.

// create list
List<Book> books = new ArrayList<Book>();
// create book objects
Book bookOne = new Book("Think and grow rich", 99, 250);
Book bookTwo = new Book("Rich Dad Poor Dad", 318, 336);
Book bookThree = new Book("Sherlock homes", 113, 536);
Book bookFour = new Book("The richest man in babylon", 129, 152);
// add objects to list
books.add(bookOne);
books.add(bookTwo);
books.add(bookThree);
books.add(bookFour);
// iterate over list
for (Book book : books) {
   System.out.println(book);
}

This prints

[Title = Think and grow rich, price = 99, pages = 250]
[Title = Rich Dad Poor Dad, price = 318, pages = 336]
[Title = Sherlock homes, price = 113, pages = 536]
[Title = The richest man in babylon, price = 129, pages = 152]

As visible from the above output, there is no ordering among book objects.

Now let us write a Comparator to sort book objects in the order of total page numbers. For writing a comparator, follow below steps.

  1. Create a class that implements java.util.Comparator interface.
  2. Implement compare() method in this class. It should contain the criteria to sort objects.
import java.util.Comparator;

public class PageOrder implements Comparator<Book> {
   public int compare(Book b1, Book b2) {
      return b1.getPages() - b2.getPages();
   }
}

compare() method defined above will sort book objects in ascending order of pages.

Now let us sort the list of books using this Comparator.

import java.util.Comparator;

public class BookSorter {
   public static void main(String... a) {
      // create list
      List<Book> books = new ArrayList<Book>();
      // create book objects
      Book bookOne = new Book("Think and grow rich", 99, 250);
      Book bookTwo = new Book("Rich Dad Poor Dad", 318, 336);
      Book bookThree = new Book("Sherlock homes", 113, 536);
      Book bookFour = new Book("The richest man in babylon", 129, 152);
      // add objects to list
      books.add(bookOne);
      books.add(bookTwo);
      books.add(bookThree);
      books.add(bookFour);
      // sort list of books
      Collectios.sort(books, new PageOrder());
      // iterate over list
      for (Book book : books) {
         System.out.println(book);
      }
   }
}

Above code will print

[Title = The richest man in babylon, price = 129, pages = 152]
[Title = Think and grow rich, price = 99, pages = 250]
[Title = Rich Dad Poor Dad, price = 318, pages = 336]
[Title = Sherlock homes, price = 113, pages = 536]

Look, the list of books is not sorted in ascending order of pages. If the list needs to be sorted in descending order of pages, then the compare() method should be modified as below.

public int compare(Book b1, Book b2) {
   return b2.getPages() - b1.getPages();
}

Now try writing a Comparator to sort the list in ascending and descending order of book price.

Anonymous Comparator
In the above example, a separate class was created for implementing a Comparator. It is not convenient and permissible to create a new class always.
In such cases, you can define a Comparator at the same place where it is required using new operator and defining compare() method.
This is called anonymous comparator and is implemented as an inner class as shown below.

Collections.sort(books, new Comparator<Book>() {
    public int compare(Book b1, Book b2) {
       return b1.getPages() - b2.getPages();
    }
});

You will only see new operator with an interface only as an anonymous class since interface in java can not be instantiated.
Comparator using Lambda expression
Starting java 8, Comparator definition can be further simplified using a Lambda expression.
compare() method can be implemented inline using its arguments and directly supplied to sort() method as shown below.

Collections.sort(books, (Book b1, Book b2) -> {
    return b1.getPages() - b2.getPages();
});

sort() can be defined as a Lambda expression because java.util.Comparator is a functional interface.
Reverse comparator
Ordering of a list by Comparator can be reversed by modifying compare() method. But many times, it is not possible to modify compare() since the source code may not be available.
In such case, the order of list may be reversed using reversed() method of java.util.Comparator.
reversed() is a default interface method added in java 8 and can be used as below.

PageOrder comparator = new PageOrder();
Collections.sort(books, comparator.reversed());

Null value comparison
If the list to be sorted contains a null value, Comparator provides its handling using following methods.
nullsFirst
It is a static interface method introduced in java 8 which accepts a comparator as argument. This method places null values before non-null values.
In other words, it considers null values to be smaller than other values.
Supply the comparator returned by this method to sort() method as shown below.

import java.util.Comparator;

public class BookSorter {
   public static void main(String... a) {
      // create list
      List<Book> books = new ArrayList<Book>();
      // create book objects
      Book bookOne = new Book("Think and grow rich", 99, 250);
      Book bookTwo = new Book("Rich Dad Poor Dad", 318, 336);
      Book bookThree = new Book("Sherlock homes", 113, 536);
      Book bookFour = new Book("The richest man in babylon", 129, 152);
      // add objects to list
      books.add(bookOne);
      books.add(bookTwo);
      books.add(bookThree);
      books.add(bookFour);
      // add null to list
      books.add(null);
      // create a comparator
      PageOrder comparator = new PageOrder();
      // create comparator to handle nulls
      Comparator<Book> nullsFirstComparator = Comparator.nullsFirst(comparator);
      // sort with null handling
      Collections.sort(books, nullsFirstComparator);
      // iterate over list
      for (Book book : books) {
         System.out.println(book);
      }
   }
}

Above code prints

null
[Title = The richest man in babylon, price = 129, pages = 152]
[Title = Think and grow rich, price = 99, pages = 250]
[Title = Rich Dad Poor Dad, price = 318, pages = 336]
[Title = Sherlock homes, price = 113, pages = 536]

See, null is printed at first position while other books are sorted in increasing order of pages which is as per the initial comparator implementation.
Above code can be shortened by writing below one liner

Collections.sort(books, Comparator.nullsFirst(new PageOrder()));

nullsLast
This method puts null values as last or it considers null values to be greater than non-null values.
It is also a static interface method accepting a comparator as argument and returns a new comparator with null values handled. Example,

import java.util.Comparator;

public class BookSorter {
   public static void main(String... a) {
      // create list
      List<Book> books = new ArrayList<Book>();
      // create book objects
      Book bookOne = new Book("Think and grow rich", 99, 250);
      Book bookTwo = new Book("Rich Dad Poor Dad", 318, 336);
      Book bookThree = new Book("Sherlock homes", 113, 536);
      Book bookFour = new Book("The richest man in babylon", 129, 152);
      // add objects to list
      books.add(bookOne);
      books.add(bookTwo);
      books.add(bookThree);
      books.add(bookFour);
      // add null to list
      books.add(null);
      // create a comparator
      PageOrder comparator = new PageOrder();
      // create comparator to handle nulls
      Comparator<Book> nullsLastComparator = Comparator.nullsLast(comparator);
      // sort with null handling
      Collections.sort(books, nullsLastComparator);
      // iterate over list
      for (Book book : books) {
         System.out.println(book);
      }
   }
}

which prints

[Title = The richest man in babylon, price = 129, pages = 152]
[Title = Think and grow rich, price = 99, pages = 250]
[Title = Rich Dad Poor Dad, price = 318, pages = 336]
[Title = Sherlock homes, price = 113, pages = 536]
null

Code above can be written as one-liner

Collections.sort(books, Comparator.nullsLast(new PageOrder()));

This article explained the concept of Comparator interface in java with examples. Hit the clap if the article was useful.