Custom Comparator in Java
Problem
A single-field Comparator can be chained with thenComparing() to add a tiebreaker field, without writing a manual multi-step comparison by hand.
Given a list of Person objects, sort them by last name, and break any ties using first name.
Java Program
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class Person {
String firstName;
String lastName;
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
public class CustomComparatorDemo {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Bob", "Smith"));
people.add(new Person("Alice", "Smith"));
people.add(new Person("Charlie", "Jones"));
Comparator<Person> byLastThenFirst = Comparator
.comparing((Person p) -> p.lastName)
.thenComparing(p -> p.firstName); // tiebreaker, only used when last names match
people.sort(byLastThenFirst);
for (Person p : people) {
System.out.println(p.lastName + ": " + p.firstName);
}
}
}Output
Core Logic
Building the primary ordering with comparing(), then chaining thenComparing() for the tiebreaker field, composes two separate comparison rules into one Comparator without any manual if/else logic.
- 1
Comparator.comparing((Person p) -> p.lastName)builds a Comparator that orders purely by last name. - 2
.thenComparing(p -> p.firstName)chains a second rule that only gets consulted when two people share the same last name. - 3
people.sort(byLastThenFirst)applies the combined Comparator to the whole list in one call. - 4The two Smiths — Alice and Bob — share a last name, so thenComparing()'s first-name rule is what decides their relative order.
[Bob Smith, Alice Smith, Charlie Jones] puts Jones first (different last name), then Alice Smith before Bob Smith (same last name, first name breaks the tie).Key Point: thenComparing() is only ever consulted when the comparator before it reports a tie (returns 0) — if every last name were unique, the first-name rule would never actually run.
Why: Chaining more fields makes each individual comparison slightly more expensive when a tie needs breaking, but the number of comparisons the sort itself performs is still O(n log n), the same as a single-field sort.