Sorting with Comparator: thenComparing and reversed
Fast recall
Quick revision
Core answer
Fluency with Java 8+ Comparator API, functional composition, and multi-key sorting
Writing verbose anonymous Comparator classes instead of Comparator.comparing chains, or misunderstanding that reversed() flips the entire chain not just the last criterion
Show Comparator.comparingInt/Long/Double for primitives (avoids boxing), mention Comparator.nullsFirst/nullsLast for null-safe sorting, and demonstrate sorting in a Stream pipeline with sorted()
Interview-ready guide
Interview answer
The Comparator fluent API replaced verbose anonymous classes — multi-key sorting is now one readable line instead of fifteen.
Fluent chaining:
Comparator.comparing(Employee::getDepartment).thenComparing(Employee::getSalary, reverseOrder())- Sorts by department ascending, then salary descending — composable and readable
Primitive specializations:
- Use
comparingInt(),comparingLong(),comparingDouble()for primitive fields comparing(Employee::getAge)auto-boxes every int to Integer — millions of unnecessary allocations in a large sort
Null handling:
nullsFirst(comparing(Employee::getName))places null names first;nullsLast()places them last- Without null handling, sorting nulls throws NPE
reversed() gotcha:
reversed()flips the entire chain, not just the last keycomparing(A).thenComparing(B).reversed()reverses both A and B — to reverse only B, passreverseOrder()to that specificthenComparing()
I recommend the fluent API as the default for all sorting — more readable, less error-prone, and primitive specializations avoid boxing overhead.
Sorting with Comparator: thenComparing and reversed
Direct Answer
Use Comparator.comparing(keyExtractor) as the base comparator, chain .thenComparing() for secondary/tertiary keys, and call .reversed() to flip any stage. These compose into a single Comparator without any anonymous class boilerplate.
Deep Dive & Architecture
compute, merge, computeIfAbsent, and computeIfPresent are Map methods (Java 8+) that atomically combine get + check + put into a single operation, replacing the verbose get-check-put anti-pattern. computeIfAbsent(key, mappingFunction) — if key absent, compute value and put; most-used for lazy initialization of multi-valued maps: map.computeIfAbsent(key, k -> new ArrayList<>()).add(value). computeIfPresent(key, remappingFunction) — if key present, recompute value (or remove if null returned). compute(key, remappingFunction) — recompute regardless of presence (function gets old value or null). merge(key, value, remappingFunction) — if absent, put value; if present, apply function to combine: map.merge(word, 1, Integer::sum) is the idiomatic word-counter. Why they matter: (1) atomic in ConcurrentHashMap — no lost updates from concurrent get-check-put races; (2) less code than if (map.containsKey(k)) map.put(k, ...) else map.put(k, ...); (3) cleaner intent — the operation says what it does. Pitfalls: (1) not atomic in non-concurrent maps (HashMap) — only the API is single-call, not thread-safe; (2) returning null from a remapping function removes the key — surprising; (3) don't recursively modify the map inside the function — undefined behavior; (4) merge doesn't handle null values the same way as compute — read the Javadoc. For interviews: lead with 'replaces get-check-put, atomic in ConcurrentHashMap, idiomatic for multimap lazy-init and counters'.
Use Comparator.comparing(keyExtractor) as the base comparator, chain .thenComparing() for secondary/tertiary keys, and call .reversed() to flip any stage. These compose into a single Comparator without any anonymous class boilerplate.
A common mistake is confusing the interface with the implementation (e.g., saying List when you mean ArrayList), or not knowing that Map does not extend Collection.
Common Mistakes & Pitfalls
Writing verbose anonymous Comparator classes instead of Comparator.comparing chains, or misunderstanding that reversed() flips the entire chain not just the last criterion
Check your understanding
Answer these without looking back at the article.
- What is the Java Collections Framework (JCF) and why does it exist? It provides a unified architecture of interfaces and implementations for storing and manipulating groups of objects, replacing the ad-hoc pre-JCF classes (Vector, Hashtable, Enumeration) with a consistent, interoperable API.
- How does HashSet work internally in Java?
- How does HashMap internally handle hash collisions in Java 8+? Explain the transition from linked list to tree.
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.