Java 44: Comparator Chaining — thenComparing, reversed & nullsFirst
easy⏱ 5 mincoursejava
comparing + thenComparing
Comparator.comparing(Person::lastName) builds a comparator from a key extractor. Chain .thenComparing(Person::firstName) to break ties — additional comparators are consulted only when the previous returns 0. This expresses 'sort by A, then B, then C' declaratively, exactly how a human describes a sort.
people.sort(
Comparator.comparing(Person::lastName)
.thenComparing(Person::firstName)
.thenComparingInt(Person::age).reversed()
);
reversed() reverses the whole chain
.reversed() flips the entire comparator built so far — a common trap. To reverse only one key, reverse that key's comparator before chaining: .thenComparing(comparing(Person::age).reversed()), or use comparing(Person::age, Comparator.reverseOrder()). Mixing ascending and descending keys requires this care.
Null-safe comparators
Comparator.nullsFirst(naturalOrder()) treats null as the smallest value; nullsLast as the largest. Wrap any comparator to make the whole comparison null-tolerant. Combined with comparing(extractor, nullsLast(naturalOrder())) you can sort by a key that may itself be null without an NPE.
Sort people by 3 keys
Given Person { last, first, age }, sort by last name ascending, then first name ascending, then age descending, with a null-safe first-name key. Use a chained comparator (model thenComparing by trying each key in order until non-zero). Print the sorted roster and confirm two 'Smith' entries break the tie by first name.
Comparators must be consistent with equals
For TreeSet/TreeMap, a comparator that returns 0 for two non-equal objects will treat them as duplicates and drop one. Keep compare(a,b)==0 ⇔ a.equals(b) for sorted collections; for plain list.sort() it's fine to compare on a subset of fields.
Quiz: stable sort guarantee
Is Collections.sort / List.sort stable? Yes — it's a TimSort variant and preserves the relative order of equal elements. That's why chaining comparators works: a first pass by name keeps relative order, and thenComparing only refines ties — but in practice you chain rather than rely on multiple passes.