Comparator Interface in Java

Naveen AnamaneniUpdated · 4 min read

The Comparator interface in Java is used to define a custom ordering for objects, written outside the class being sorted. While Comparable gives a class one natural order, you can create any number of comparators: one to sort students by name, another by marks, another by marks in reverse. A comparator is passed to methods such as list.sort, Collections.sort and new TreeSet<>(comparator). In this lesson, you will learn how to write comparators, from a full class to a one-line lambda.

Declaration

Declaration
@FunctionalInterface
public interface Comparator<T> {
    int compare(T a, T b);
    // plus many default and static helper methods
}

Comparator is in the java.util package. compare(a, b) follows the same sign rule as compareTo: negative if a comes first, zero if equal, positive if b comes first.

Why Use Comparator

  • To sort a class in more than one way.
  • To sort a class you cannot change, such as one from a library.
  • To sort in an order different from the natural one, such as strings by length.

Example: Three Ways to Write a Comparator

Let's see an example that sorts the same list using a class, an anonymous class and a lambda.

ComparatorWays.java▶ Try it
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class ComparatorWays {
    static class ByLength implements Comparator<String> {
        @Override
        public int compare(String a, String b) {
            return Integer.compare(a.length(), b.length());
        }
    }
 
    public static void main(String[] args) {
        List<String> names = new ArrayList<>(
            List.of("Srihitha", "Navika", "Advitha", "Anamaneni"));
 
        names.sort(new ByLength());
        System.out.println("By length: " + names);
 
        names.sort(new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.compareTo(a);
            }
        });
        System.out.println("Reverse:   " + names);
 
        names.sort((a, b) -> a.compareTo(b));
        System.out.println("Natural:   " + names);
    }
}
Output
By length: [Navika, Advitha, Srihitha, Anamaneni]
Reverse:   [Srihitha, Navika, Anamaneni, Advitha]
Natural:   [Advitha, Anamaneni, Navika, Srihitha]

Explanation: ByLength is a normal class that compares string lengths. The anonymous class reverses the order by comparing b with a. The lambda (a, b) -> a.compareTo(b) is the shortest form; it works because Comparator is a functional interface with one abstract method.

Helper Methods of Comparator

MethodDescription
static comparing(Function key)It creates a comparator that compares by the given key, such as Student::name.
static comparingInt(ToIntFunction key)It compares by an int key without boxing.
reversed()It returns a comparator with the opposite order.
thenComparing(...)It adds a second comparison for ties.
static naturalOrder()It returns the natural (Comparable) order.
static reverseOrder()It returns the reverse of the natural order.
static nullsFirst(Comparator c)It puts null values before all others.
static nullsLast(Comparator c)It puts null values after all others.

Example: Comparator.comparing with a Record

ComparingDemo.java▶ Try it
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class ComparingDemo {
    record Student(String name, int marks) { }
 
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>(List.of(
            new Student("Navika", 91),
            new Student("Srihitha", 78),
            new Student("Advitha", 85)));
 
        list.sort(Comparator.comparing(Student::name));
        System.out.println(list);
 
        list.sort(Comparator.comparingInt(Student::marks).reversed());
        System.out.println(list);
    }
}
Output
[Student[name=Advitha, marks=85], Student[name=Navika, marks=91], Student[name=Srihitha, marks=78]]
[Student[name=Navika, marks=91], Student[name=Advitha, marks=85], Student[name=Srihitha, marks=78]]

Explanation: Comparator.comparing(Student::name) builds a comparator from the name of each student. comparingInt(Student::marks).reversed() sorts by marks from highest to lowest. No compare method had to be written by hand.

Common Mistakes

Writing (a, b) -> a.marks() - b.marks(). Subtraction can overflow; use comparingInt.

Calling reversed() in the wrong place in a chain. It reverses everything before it (see "Comparator Chaining in Java").

Sorting a list that contains null. Wrap the comparator in Comparator.nullsFirst(...) or nullsLast(...).

Interview Corner

What is the Comparator interface?

Comparator<T> in java.util is a functional interface whose method int compare(T a, T b) defines an ordering outside the class being compared. It lets you sort objects in many different ways, or sort classes you can't modify. It provides helpers such as comparing, comparingInt, thenComparing, reversed, naturalOrder, nullsFirst and nullsLast, and is usually written as a lambda or method reference.

Practice

  1. Easy: Sort a list of five city names by their last letter using a lambda comparator.
  2. Harder: Sort a List<Student> by name length, then print it again sorted by marks from lowest to highest, using only Comparator helper methods.

Quick check: Comparator

4 questions
1Which package is Comparator in?
2How many comparators can you create for one class?
3What does Comparator.comparing(Student::name) do?
4Which method flips a comparator's order?

Key Takeaways

Was this tutorial helpful?

Lesson 268 of 482