How do you build a multi-level (tie-breaking) sort using thenComparing, and what determines the order of the chain?
answer
- thenComparing = tie-breaker, used only when previous returns 0
- chain order = priority order (first link = primary key)
- key-extractor and full-Comparator overloads; thenComparingInt for primitives
- reversed() flips the WHOLE chain, not just the last link
- object sort is stable (TimSort) - true ties keep input order
basics
~20 sthenComparing adds a backup rule used only when the first comparator says two items are equal. You chain them in priority order: first comparator is primary, the next breaks ties, and so on - like sorting by last name, then by first name.
solid answer
~40 sthenComparing chains comparators into a lexicographic, multi-level ordering. The base comparator decides first; only when it returns zero (a tie) does the next comparator in the chain get consulted, and so on down the chain. So the chain order is priority order: the first is primary, each subsequent one is the next tie-breaker. thenComparing accepts either a full Comparator or a key extractor (with thenComparingInt/Long/Double variants to avoid boxing for primitive keys). The result is itself a Comparator, so you can keep chaining and apply reversed() - though watch out: reversed() flips the *entire* accumulated comparator, not just the last link. A typical example: Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName).thenComparingInt(Person::getAge).
go deeper
Can chain thenComparing to add a second sort key and knows it breaks ties.
Orders the chain by priority, uses thenComparingInt for primitives, and knows the reversed()-reverses-everything trap.
Explains lexicographic semantics, per-level reversal techniques, and stability (TimSort) implications for ties.
Designs reusable layered comparators, reasons about stability guarantees in pipelines, and the cost of repeated key extraction across links.
## Multi-level sorting Real sorts usually need tie-breakers: sort employees by department, then by salary, then by name. `thenComparing` builds exactly this. ### How it works ```java Comparator<Person> cmp = Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName); ``` When comparing two people, `cmp.compare(a, b)`: 1. Runs the **primary** comparator (last name). If it returns non-zero, that result wins - done. 2. **Only if** the primary returned **zero** (last names equal) does it consult the **next** comparator (first name), and return its result. This is **lexicographic** ordering, exactly like dictionary ordering of words: compare first letters; only on a tie look at the second; etc. So **chain order = priority order**: the earliest link is the most significant sort key. ### Two ways to pass the tie-breaker `thenComparing` is overloaded: ```java .thenComparing(otherComparator) // a full Comparator .thenComparing(Person::getFirstName) // a key extractor (natural key order) .thenComparing(Person::getCity, cityCmp) // key extractor + key comparator .thenComparingInt(Person::getAge) // primitive key, no boxing ``` Use `thenComparingInt/Long/Double` for primitive keys to avoid autoboxing, just like `comparingInt`. ### `reversed()` reverses the WHOLE chain A classic trap: ```java Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .reversed(); ``` `reversed()` flips the **entire** comparator built so far - so this reverses both last- and first-name order. To reverse only one level, reverse that level individually: ```java Comparator.comparing(Person::getLastName, Comparator.reverseOrder()) .thenComparing(Person::getFirstName); // last name desc, first name asc ``` or ```java Comparator.comparing(Person::getLastName) .thenComparing(Comparator.comparing(Person::getAge).reversed()); ``` ### Stability Java's `List.sort`/`Arrays.sort` for objects is a **stable** sort (TimSort): elements that the comparator deems equal keep their original relative order. So if your chain leaves some ties truly equal, their pre-existing order is preserved - useful when the input was already meaningfully ordered. ### Putting it together ```java people.sort( Comparator.comparing(Person::getDepartment) .thenComparingInt(Person::getSalary) .thenComparing(Person::getLastName)); ``` Department first; equal departments broken by salary; equal salaries broken by last name.
- You wrote comparing(A).thenComparing(B).reversed() but only wanted B reversed. What happened and how do you fix it?reversed() flipped the entire chain, so both A and B are descending. Reverse only B by passing a reversed key comparator at that level, e.g. thenComparing(Comparator.comparing(B).reversed()), or reverse A's level and leave B ascending depending on intent.
- If two elements are equal across every link in the chain, what determines their final order?Nothing in the comparator - they're ties. Java's object sort is stable (TimSort), so they retain their original relative input order.
saying these in an interview costs you the question
- Thinking reversed() only reverses the last thenComparing
- Putting the tie-breaker before the primary key (wrong priority)
- Assuming thenComparing runs always (it only runs on a tie)
- Using thenComparing(extractor) for a primitive key (boxes; use thenComparingInt)