Comparator with lambdas in Java
Comparator.comparing, nullsFirst, chaining.
Comparator.comparing(p -> p.name()).reversed() refuses to compile, yet Comparator.comparing(P::name).reversed() works fine. Let's master Comparators, and solve that mystery.Sort rules as one-liners
**Comparator.comparing(keyExtractor) builds a comparator from a function that extracts the sort key, then compares keys by natural order. comparingInt avoids boxing. thenComparing breaks ties, and reversed()** flips the order.
people.sort(Comparator
.comparing(Person::city)
.thenComparingInt(Person::age)
.reversed());Your turn
What does this print?
List<String> words = new ArrayList<>(
List.of("pear", "kiwi", "banana", "fig"));
words.sort(Comparator.comparing(String::length)
.thenComparing(Comparator.reverseOrder()));
System.out.println(words);[fig, kiwi, pear, banana][fig, pear, kiwi, banana][banana, pear, kiwi, fig]
Show the answer
Shortest first: fig (3). Then the 4-letter tie, broken in reverse alphabetical order: pear before kiwi. Then banana (6).
Nulls crash natural order
**Comparator.naturalOrder()** calls compareTo on the elements. If one of them is null, calling a method on it throws **NullPointerException**.
// compares a.compareTo(b)
// a == null → NullPointerExceptionA null in the list
What happens?
List<String> names = new ArrayList<>(
Arrays.asList("cy", null, "ab"));
names.sort(Comparator.naturalOrder());
System.out.println(names);[null, ab, cy][ab, cy, null]Throws NullPointerException
Show the answer
Sooner or later, natural order compares against the null element and calls a method on it: **NullPointerException**.
nullsFirst / nullsLast
**Comparator.nullsFirst(cmp) and nullsLast(cmp) wrap another comparator. They handle nulls themselves** and only pass non-null pairs to the wrapped comparator.
names.sort(Comparator.nullsFirst(
Comparator.naturalOrder()));
// [null, ab, cy]Nulls to the back
What does this print?
List<String> names = new ArrayList<>(
Arrays.asList("cy", null, "ab"));
names.sort(Comparator.nullsLast(
Comparator.naturalOrder()));
System.out.println(names);[null, ab, cy][ab, cy, null]Throws NullPointerException
Show the answer
nullsLast puts the null at the end and sorts the rest naturally: [ab, cy, null].
Lambdas lose their type before .reversed()
Type inference doesn't flow back through a chained call. In comparing(p -> p.name()).reversed(), comparing can't see its target type, so p becomes **Object** and p.name() doesn't compile. Use a method reference or an explicit parameter type.
comparing(p -> p.name()).reversed(); // ✗
comparing(P::name).reversed(); // ✓
comparing((P p) -> p.name()).reversed(); // ✓Every sortable screen
Product lists by price then rating, inboxes newest first, user tables with missing last names: real data is messy and often contains nulls. comparing, thenComparing, reversed and nullsLast handle it in one readable line, with no hand-written compare method.
Key takeaways
- comparing(Person::name), comparingInt(Person::age) avoids boxing
- thenComparing(...) breaks ties
- nullsFirst(naturalOrder()) puts nulls at the front
- Lambdas lose type inference before .reversed() — prefer method refs
Comparator.comparing, thenComparing, nullsFirst and nullsLast all arrived in Java 8 as static and default methods. Before that, every multi-key sort meant a hand-written compare method full of if statements.
Practice questions
What does this print?
List<String> words = new ArrayList<>(
List.of("kiwi", "fig", "apple", "date"));
words.sort(Comparator.comparing(String::length)
.thenComparing(Comparator.reverseOrder()));
System.out.println(words);- [fig, date, kiwi, apple]
- [fig, kiwi, date, apple]
- [apple, kiwi, date, fig]
- [fig, kiwi, apple, date]
Check your answer
[fig, kiwi, date, apple]. Shortest first: fig (3), then the 4-letter tie kiwi/date in reverse alphabetical order (kiwi before date), then apple (5).
What happens when this runs?
List<String> names = new ArrayList<>(
Arrays.asList("bo", null, "al"));
names.sort(Comparator.naturalOrder());
System.out.println(names);- [null, al, bo]
- [al, bo, null]
- Throws NullPointerException
- [al, bo]
Check your answer
Throws NullPointerException. naturalOrder calls compareTo on the elements, and calling a method on null throws NullPointerException.