Comparator Chaining in Java

Naveen AnamaneniUpdated · 4 min read

Comparator chaining in Java is a way of sorting by more than one key: first by one property, and when two elements are equal on that property, by a second one, then a third. It is done with the thenComparing method of Comparator. A familiar example is a class list sorted by marks from highest to lowest, with students who have the same marks listed alphabetically. In this lesson, you will learn how to build such chains and how reversed behaves inside them.

Syntax

Syntax
Comparator<T> c = Comparator.comparing(firstKey)
                            .thenComparing(secondKey)
                            .thenComparing(thirdKey);

The second comparison runs only when the first one says "equal", the third only when both say "equal", and so on.

Methods Used in Chaining

MethodDescription
thenComparing(Function key)It compares by another key when the earlier comparison is a tie.
thenComparing(Comparator c)It uses another full comparator for ties.
thenComparingInt(ToIntFunction key)It compares ties by an int key.
reversed()It reverses the whole chain built so far.
Comparator.reverseOrder()It gives reverse natural order, useful inside one link.

Example: Marks High to Low, Then Name

Let's see an example where two students tie on marks.

ChainDemo.java▶ Try it
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class ChainDemo {
    record Student(String name, String city, int marks) { }
 
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>(List.of(
            new Student("Srihitha", "Pune", 85),
            new Student("Navika", "Delhi", 91),
            new Student("Anamaneni", "Pune", 85),
            new Student("Advitha", "Delhi", 85)));
 
        list.sort(Comparator.comparingInt(Student::marks).reversed()
                            .thenComparing(Student::name));
        list.forEach(s -> System.out.println(s.marks() + " " + s.name()));
    }
}
Output
91 Navika
85 Advitha
85 Anamaneni
85 Srihitha

Explanation: The first comparator sorts by marks from highest to lowest, so Navika (91) is first. Three students have 85, which is a tie, so thenComparing(Student::name) orders those three alphabetically.

How reversed() Works in a Chain

reversed() reverses everything before it, not just the last key. This is a common surprise:

ReversedChain.java▶ Try it
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class ReversedChain {
    record Student(String name, String city, int marks) { }
 
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>(List.of(
            new Student("Srihitha", "Pune", 85),
            new Student("Navika", "Delhi", 91),
            new Student("Advitha", "Delhi", 85)));
 
        list.sort(Comparator.comparing(Student::city)
                            .thenComparing(Student::name)
                            .reversed());
        System.out.println("Whole chain reversed: "
            + list.stream().map(Student::name).toList());
 
        list.sort(Comparator.comparing(Student::city)
                            .thenComparing(Student::name,
                                           Comparator.reverseOrder()));
        System.out.println("Only name reversed:   "
            + list.stream().map(Student::name).toList());
    }
}
Output
Whole chain reversed: [Srihitha, Navika, Advitha]
Only name reversed:   [Navika, Advitha, Srihitha]

Explanation: In the first sort, reversed() flipped both the city and the name order, so Pune came before Delhi. In the second, thenComparing(key, Comparator.reverseOrder()) reversed only the names inside each city: Delhi still comes first, with Navika before Advitha.

Handling null Keys

If a key may be null, wrap its comparator:

Nulls.java▶ Try it
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
 
public class Nulls {
    public static void main(String[] args) {
        List<String> cities = new ArrayList<>(
            Arrays.asList("Pune", null, "Delhi"));
        cities.sort(Comparator.nullsLast(Comparator.naturalOrder()));
        System.out.println(cities);
    }
}
Output
[Delhi, Pune, null]

Explanation: nullsLast puts null at the end and sorts the rest with natural order. Without it, the sort would throw NullPointerException.

Common Mistakes

Putting reversed() at the end when only the last key should be reversed. Use Comparator.reverseOrder() inside that link.

Starting the chain with a lambda and calling reversed(). Java may fail to infer the type; write (Student s) -> ... or use a method reference.

Chaining several sort calls instead of one chain. It works only by luck of stable sorting; one chained comparator is clearer.

Practice

  1. Easy: Sort a list of students by city, then by marks from highest to lowest.
  2. Harder: Sort a list of products by category, then by price from lowest to highest, then by name, where some categories are null and must come last.

Quick check: Comparator chaining

4 questions
1When does thenComparing run?
2What does reversed() at the end of a chain reverse?
3How do you reverse only the second key?
4Which helper puts null values at the end?

Key Takeaways

Was this tutorial helpful?

Lesson 270 of 482