🧠 Data Structures & Algorithms · Advanced

How Java sorts

Dual-pivot quicksort for primitives, TimSort (stable) for objects.

🧩 The mysteryArrays.sort is one method name, but behind it Java runs two completely different algorithms. Knowing which one runs can change your output.

Two algorithms, one name

Arrays of primitives (int[], double[]...) are sorted with dual-pivot quicksort: very fast, not stable. Arrays of objects (String[], Integer[]...) and List.sort use TimSort: stable, O(n log n) in the worst case.

int[] nums = {3, 1, 2};
Arrays.sort(nums);   // dual-pivot quicksort
String[] words = {"b", "a"};
Arrays.sort(words);  // TimSort
list.sort(null);     // TimSort, natural order

Why primitives may be unstable

Two equal ints are indistinguishable: there's no "original order" anyone could observe, so Java can use the faster unstable algorithm. Objects are different: two people can both be 30 but have different names, and callers may rely on their order. So objects get a stable sort.

🔮 Predict it

Stable in action

Sorting by age only. What does this print?

record P(String name, int age) {}
void main() {
    var ps = new ArrayList<>(List.of(
        new P("Mia", 40), new P("Ann", 22),
        new P("Leo", 22), new P("Kai", 40)));
    ps.sort(Comparator.comparingInt(P::age));
    for (P p : ps) IO.print(p.name() + " ");
}
  1. Ann Leo Mia Kai
  2. Ann Leo Kai Mia
  3. Leo Ann Mia Kai
Show the answer

Ann and Leo (22) come first, then Mia and Kai (40). Within each age, List.sort is stable, so names keep their original order: Ann before Leo, Mia before Kai.

TimSort's trick

Real data is often partly sorted. TimSort finds existing sorted runs, extends short ones with insertion sort, and merges runs like merge sort. On already sorted input it does about n comparisons; its worst case is still O(n log n).

🤔 Think first

Two keys, two passes

You want employees by department, and by name within each department, using a stable single-key sort twice. Which key do you sort by first?

Think about it, then reveal the answer

Sort by name first, then by department. The last sort decides the primary order, and stability keeps the earlier name order within each department. In real code, do it in one pass: Comparator.comparing(Emp::dept).thenComparing(Emp::name).

⚠️ The trap

No Comparator for int[]

This is a compile error. The Comparator overloads take T[], and generics can't use primitive types, so there's no Comparator<int>. Sort ascending and reverse it, or use an Integer[].

int[] a = {3, 1, 2};
Arrays.sort(a, Comparator.reverseOrder());
// error: no suitable method found
💼 In the real world

Why it matters at work

Stability is why sorting a table by one column, then another, behaves predictably in UIs. Knowing that List.sort is stable and O(n log n) lets you rely on it; knowing primitives use quicksort explains why there's no comparator overload for int[].

Key takeaways

  1. int[], double[]…: dual-pivot quicksort (not stable)
  2. Object[] and List.sort: TimSort (stable)
  3. TimSort finds existing sorted runs and merges them
  4. Stability lets you sort by one key, then by another

💡 TimSort is a librarian who notices shelves already in order and only merges them.

🤯 Did you know?

Tim Peters created TimSort for Python in 2002, and Java adopted it for objects in Java 7. In 2015, researchers using formal verification found a bug in TimSort in both Java and Python.

Practice questions

What does this print?

record P(String name, int age) {}
void main() {
    var ps = new ArrayList<>(List.of(
        new P("Zed", 30), new P("Amy", 25),
        new P("Bob", 30)));
    ps.sort(Comparator.comparingInt(P::age));
    for (P p : ps) IO.print(p.name() + " ");
}
  1. Amy Bob Zed
  2. Amy Zed Bob
  3. Zed Bob Amy
  4. Bob Zed Amy
Check your answer

Amy Zed Bob. Amy (25) comes first. Zed and Bob are both 30, and TimSort is stable, so they keep their original order: Zed before Bob.

Which algorithm does Arrays.sort(String[]) use?

  1. Dual-pivot quicksort
  2. TimSort
  3. Heap sort
  4. Bubble sort
Check your answer

TimSort. Object arrays use TimSort, which is stable and adapts to existing runs. Only primitive arrays use dual-pivot quicksort.

Next: undo buttons, bracket checkers and BFS all run on two humble structures, and Java's Stack class isn't the one to use.