# sorting Companion code for the ankurm.com post *"Java Comparator & Comparable: The Complete Guide."* Module in `java-core-examples`, the Java-core series. ## Versions this was built and tested against | Component | Version | Notes | |---|---|---| | JDK | 25.0.4.1+1 (Temurin, LTS) | `Comparator.comparing`/`thenComparing`/`nullsFirst`/`nullsLast` shipped in Java 8 (2014), unchanged since. Records (used here as sort keys) shipped in Java 16 (2021). | | JUnit Jupiter | 5.11.0 | | | Maven | 3.9.11 | | ## Quickstart ```bash export JAVA_HOME=/path/to/jdk-17-or-newer mvn compile java -cp target/classes com.ankurm.sorting.IntegerOverflowBugDemo ``` `scripts/run-all.sh` regenerates every file in `output/`. `scripts/run.sh ` runs one demo ad hoc. ## What's in here | File | What it shows | |---|---| | `Employee.java` | A plain POJO that deliberately does NOT implement `Comparable` - it has no one obvious order, which is the case `Comparator` exists for. | | `Product.java` | A POJO that DOES implement `Comparable` - price is its one obvious natural order - using `Double.compare`, never subtraction. | | `EmployeeNameComparator.java` + `OldSchoolComparatorDemo.java` | Pre-Java-8 Comparator construction: a named class and an anonymous inner class, run side by side. | | `ComparableNaturalOrderDemo.java` | `Collections.sort(products)` calling `Product.compareTo()` automatically. | | `ComparatorFactoryMethodsDemo.java` | Lambdas, `Comparator.comparing`/`comparingInt`/`comparingDouble`, `thenComparing`, `reversed`, `nullsFirst`/`nullsLast`, chained together. | | `TreeSetOrderingDemo.java` | The same element type sorted two ways in a `TreeSet` - natural order from the no-arg constructor, a reversed custom order from the Comparator constructor. | | `StockPrice.java` + `RecordOrderingDemo.java` | A record implementing `Comparable`; its accessor is `price()` not `getPrice()`; and the record-specific trap where a `compareTo` that does not use every component stops being consistent with the record's generated `equals()` - demonstrated by a `TreeSet` silently dropping an element. | | `Ticket.java` + `IntegerOverflowBugDemo.java` | **The main exhibit.** `(a, b) -> a.val - b.val` reproduced exactly, sorted against `Integer.compare`, with the actual wrong order it produces for `Integer.MAX_VALUE` / `Integer.MIN_VALUE` input, checked independently of either comparator. | | `SortingTest.java` | Pins all of the above as assertions, 9/9 passing - including the overflow bug and the record/equals inconsistency as regression tests, not just printouts. | | `output/01-07` | Captured runs of all six demos plus the test suite. | ## Notes worth knowing before reading the post - **The subtraction comparator does not crash. It just lies.** `output/06` is the full transcript: `(a,b) -> a.val - b.val` sorts `[A(10), B(MAX), C(-20), D(MIN), E(0), F(5), G(-1)]` into `[G(-1), E(0), F(5), A(10), B(MAX), D(MIN), C(-20)]` - `B(MAX)` sitting directly in front of `D(MIN)`, and `C(-20)` landing dead last. `Integer.compare` sorts the exact same list correctly. Both are real `List.sort()` calls against the same seven elements - see `output/06` and `SortingTest#subtractionComparatorProducesWrongOrderOnOverflow`. - **A record's generated `equals()` uses every component. Your `compareTo()` doesn't have to.** If it doesn't, `compareTo(x) == 0` stops implying `equals(x)` is true, and anything keyed on `compareTo` - most importantly `TreeSet`/`TreeMap` - starts silently treating distinct records as duplicates. See `output/05`, section 4. - **`Collections.sort` / `List.sort` never threw here**, even with `Integer.MAX_VALUE` and `Integer.MIN_VALUE` in a seven-element list. Java's sort implementation only runs its contract-violation detector once a run is long enough to need merging (`TimSort`'s `MIN_MERGE`, 32 elements by default); below that it is a plain insertion sort that will happily produce a wrong-but-silent answer. A small reproduction case like this one will not save you from the bug in production - it will just fail to warn you in a unit test with a short list. ## License MIT - see the [repo-wide LICENSE](../LICENSE).