Comparators in Depth
Sort by one key, break ties with another, reverse it, and survive a null.
Open this lesson in the learning hubKey points
Comparator.comparing(Track::artist)builds a comparator from a key extractor. That is most sorting.- Chain tie-breakers with
thenComparing. Each one only runs when everything before it compared equal. reversed()flips everything to its left, soa.thenComparing(b).reversed()reverses both keys.- Use
comparingInt,comparingLongorcomparingDoublefor primitive keys: no boxing per compare. nullsFirstandnullsLastwrap a comparator so a null sorts instead of throwing.sorted()is stable: equal elements keep their original order, which is what makes chaining work.
Example
import java.util.*;
import java.util.stream.*;
public class Main {
record Track(String artist, String title, int seconds) {}
public static void main(String[] args) {
List<Track> tracks = List.of(
new Track("Ada", "Zeta", 210),
new Track("Ada", "Alpha", 185),
new Track("Bo", "Mu", 185),
new Track("Bo", "Nu", 320));
Comparator<Track> byArtist = Comparator.comparing(Track::artist);
Comparator<Track> byLength = Comparator.comparingInt(Track::seconds);
show("artist, then title ", tracks, byArtist.thenComparing(Track::title));
show("longest first ", tracks, byLength.reversed());
show("reversed BOTH keys ", tracks, byArtist.thenComparing(Track::title).reversed());
show("artist, longest first", tracks, byArtist.thenComparing(byLength.reversed()));
// A null blows up the sort unless the comparator is wrapped.
List<String> withNull = Arrays.asList("pear", null, "fig");
System.out.println("nullsFirst : " + withNull.stream()
.sorted(Comparator.nullsFirst(Comparator.naturalOrder())).toList());
try {
withNull.stream().sorted(Comparator.naturalOrder()).toList();
} catch (NullPointerException e) {
System.out.println("plain sort : NullPointerException");
}
}
static void show(String label, List<Track> tracks, Comparator<Track> c) {
System.out.println(label + ": " + tracks.stream().sorted(c)
.map(Track::title).collect(Collectors.joining(", ")));
}
}
comparing, then thenComparing, and remember reversed() flips the whole chain before it.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.