LESSON 35 · INTERMEDIATE4 min read

Hybrid and Non-Comparison Sorting: TimSort Invariants and Radix Sort

Deconstruct the internal workings of Java's Arrays.sort: TimSort run-merging heuristics, non-comparison Radix Sort, and functional Comparator composition chains.

OpenJDK 21+ (LTS)Free to readModule 9 of 15
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Analyze TimSort's galloping mode, natural run identification, and stack balance invariants
  • Implement LSD (Least Significant Digit) Radix Sort to achieve linear O(d * (n + k)) sorting
  • Compose complex multi-field Comparators using modern Java 21 functional chaining

01TimSort: Java's Default Object Sorting Engine

When you invoke Arrays.sort(Object[]) or Collections.sort(List) in Java, the underlying implementation is TimSort—an adaptive hybrid sorting algorithm created by Tim Peters in 2002 for Python and adopted by HotSpot in Java 7. Unlike pure quicksort, TimSort is stable: equal elements preserve their original relative order.

TimSort exploits a fundamental property of real-world data: datasets are rarely randomly shuffled; they almost always contain preexisting subsequences that are already sorted in ascending or strictly descending order (called natural 'runs').

TimSort scans the array to identify these natural runs. Descending runs are reversed in-place in linear time. If a run is shorter than a threshold minRun (typically 32 to 64 elements), TimSort extends the run using binary insertion sort. It then pushes runs onto a stack, merging them according to strict stack invariants to ensure balanced O(n log n) merging.

notes.txtPlain text
Stack of pending runs: [..., Run A, Run B, Run C] (C is top)Invariant 1: Length(A) > Length(B) + Length(C)Invariant 2: Length(B) > Length(C)If either invariant is violated:  If Length(A) < Length(C), merge B with A;  Else merge B with C.Result: The stack height is guaranteed to be O(log n), preventing stack overflowsand ensuring merges operate on roughly equal-sized sub-arrays!

02Radix Sort: Breaking the Comparison Lower Bound

A foundational theorem of computer science states that any comparison-based sorting algorithm (Merge Sort, Quick Sort, Heap Sort) requires Omega(n log n) comparisons in the worst case. Non-comparison sorting algorithms bypass this theoretical lower bound by exploiting the digit representations of keys.

Radix Sort processes keys digit by digit. In Least Significant Digit (LSD) Radix Sort, numbers are sorted starting from the 1s place, then the 10s place, then 100s, and so forth, using a stable sorting subroutine like Counting Sort on each pass.

For n numbers with maximum d digits in base b (typically base 256 for bytes or base 10 for decimals), LSD Radix Sort runs in O(d * (n + b)) time. When d is small and constant, Radix Sort achieves true linear O(n) runtime—making it standard in high-frequency trading tick processors and network packet sorters.

example.js☕Java
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

03Functional Comparator Composition in Java 21

Modern enterprise applications frequently sort complex business entities by multiple nested attributes with varying nullability rules. Legacy Java required writing verbose anonymous classes with nested if-else comparison logic.

In Java 21, we construct high-performance, type-safe comparators using functional composition: Comparator.comparing(), thenComparing(), and nullsLast(). The JVM optimizes these lambdas into efficient inline method calls.

example.js☕Java
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

04Sorting Algorithm Performance Characteristics

Here is a comprehensive reference matrix comparing foundational sorting algorithms across time complexity, auxiliary memory, stability, and real-world deployment contexts:

Notice that TimSort provides the best of all worlds for object sorting: guaranteed O(n log n) worst-case, O(n) best-case on partially sorted inputs, and absolute stability.

notes.txtPlain text
+------------+------------+------------+------------+---------+----------------------------+| Algorithm  | Best Time  | Avg Time   | Worst Time | Space   | Stable? | Modern Java Deployment     |+------------+------------+------------+------------+---------+---------+----------------------------+| TimSort    | O(n)       | O(n log n) | O(n log n) | O(n)    | YES     | Arrays.sort(Object[])      || Dual-Pivot | O(n log n) | O(n log n) | O(n^2)     | O(log n)| NO      | Arrays.sort(primitive[])   || HeapSort   | O(n log n) | O(n log n) | O(n log n) | O(1)    | NO      | Embedded / Hard Real-Time  || RadixSort  | O(d*n)     | O(d*n)     | O(d*n)     | O(n+k)  | YES     | Fixed-length integer keys  |+------------+------------+------------+------------+---------+---------+----------------------------+
LET IT CLICK

The ideas worth keeping.

  • TimSort balances stability and speed by identifying natural runs and merging them with stack invariants.
  • LSD Radix Sort achieves O(d * n) linear sorting for fixed-length keys using stable digit passes.
  • Functional Comparator chaining provides clean, declarative multi-level sorting in modern Java.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Exercise 1: Construct a Comparator for a Transaction reco

Construct a Comparator for a Transaction record (String currency, double amount, long timestamp) sorting primarily by currency ASC, then amount DESC, and finally timestamp ASC.

your_turn_1.java☕Java
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.javaJava
public record Transaction(String currency, double amount, long timestamp) {}public class TransactionSorter {    public static java.util.Comparator<Transaction> buildComparator() {        return java.util.Comparator            .comparing(Transaction::currency)            .thenComparing(java.util.Comparator.comparingDouble(Transaction::amount).reversed())            .thenComparingLong(Transaction::timestamp);    }}

Use Comparator.comparing(), thenComparing(), thenComparingLong(), and .reversed().

02

Exercise 2: Why must the inner digit loop in LSD Radix So

Why must the inner digit loop in LSD Radix Sort iterate backwards through the array during the output array population phase?

your_turn_2.java☕Java
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.javaJava
// In LSD Radix Sort, earlier sorted lower-order digits must preserve their relative order// when sorting by higher-order digits. Because count[digit] represents the cumulative ending index,// traversing backwards places identical digits into the output array from right to left,// which maintains stability. Traversing forward would reverse the relative order and corrupt the sort.

Think about stability: how do elements with identical current digits maintain their previous order?

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01Which sorting algorithm is used by default in Java for Arrays.sort(Object[])?
02Why does Radix Sort avoid the Omega(n log n) lower bound for sorting?
03What algorithm does Java use when sorting primitive arrays like int[] or double[]?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Exercise 1: Construct a Comparator for a Transaction reco

Construct a Comparator for a Transaction record (String currency, double amount, long timestamp) sorting primarily by currency ASC, then amount DESC, and finally timestamp ASC.

EXPECTED OUTPUT
Build successful. (Java 15)
[Process exited normally]
Main.javaJava
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this lesson, when you want a second angle or the full details.

One lesson closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock lesson completion.