Back to Sorting & Searching
Q9Question 9 of 22

What is Sorting?

Key Concepts
  • Sorting rearranges a collection into a defined order using a comparison rule captured by Comparable or Comparator.
  • A sorting algorithm is characterised by time complexity, space complexity, and stability.
  • Comparison-based sorts have a proven lower bound of O(n log n); non-comparison sorts can reach O(n) for limited ranges.
  • Stability means equal elements keep their original relative order, which matters for multi-key sorting.
  • Java uses a stable TimSort for objects (Collections.sort) and a dual-pivot quicksort for primitives (Arrays.sort).
  • Simple sorts (bubble, selection, insertion) are O(n^2); efficient sorts (merge, quick, heap) are O(n log n).
  • In production, use the library sort and reserve hand-written algorithms for interviews or custom structures.

Definition

Sorting rearranges a collection into a defined order using a comparison rule.
  • Sorting – the process of arranging elements of a collection into ascending or descending order according to a comparison rule.
  • Comparison rule – captured by Comparable (natural order via compareTo) or Comparator (external ordering).
  • Stability – a stable sort preserves the original relative order of equal elements.
  • Lower bound – comparison-based sorts cannot beat O(n log n) in the worst case; this is a proven mathematical limit.
  • Non-comparison sorts – counting sort and radix sort bypass the lower bound for data with limited range, achieving O(n).

Picture the National Bank of India preparing a statement for Rajesh that lists all his transactions. Nobody wants them in random order. The bank arranges them by date from oldest to newest, or by amount from largest to smallest. That act of rearranging a jumbled pile into a clean, ordered sequence by some rule is sorting. The rule you choose and the direction define the result.

Understanding the Concepts

What is sorting?

Sorting is the process of arranging the elements of a collection into a defined order, typically ascending or descending, according to a comparison rule. Every sorting algorithm must answer the question: given two elements, which one comes first?

In Java, this comparison is captured by the Comparable interface for natural order and the Comparator interface for external order.

JAVA
package com.bank.sort;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class SortingBasics {

    record Transaction(String id, BigDecimal amount, String date) {}

    public static void main(String[] args) {
        List<Transaction> txns = new ArrayList<>(List.of(
            new Transaction("T1", new BigDecimal("50000"), "2024-01-15"),
            new Transaction("T2", new BigDecimal("25000"), "2024-01-10"),
            new Transaction("T3", new BigDecimal("75000"), "2024-01-12")
        ));

        // Sort by amount ascending
        txns.sort(Comparator.comparing(Transaction::amount));
        System.out.println("By amount:");
        txns.forEach(t -> System.out.println("  " + t.id() + " " + t.amount()));

        // Sort by date descending
        txns.sort(Comparator.comparing(Transaction::date).reversed());
        System.out.println("By date desc:");
        txns.forEach(t -> System.out.println("  " + t.id() + " " + t.date()));
    }
}
TEXT
By amount:
  T2 25000
  T1 50000
  T3 75000
By date desc:
  T1 2024-01-15
  T3 2024-01-12
  T2 2024-01-10

Why does stability matter?

A stable sort preserves the original relative order of elements that compare as equal. This is critical in multi-key sorting. If you sort transactions first by date and then by amount, a stable sort keeps the date order within each amount group. An unstable sort may scramble it.

JAVA
package com.bank.sort;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class StabilityMatters {

    record Transaction(String id, int amount, String date) {}

    public static void main(String[] args) {
        List<Transaction> txns = new ArrayList<>(List.of(
            new Transaction("T1", 50000, "Jan-10"),
            new Transaction("T2", 50000, "Jan-12"),
            new Transaction("T3", 50000, "Jan-15")
        ));

        // All amounts equal: stable sort keeps T1, T2, T3 in original order
        txns.sort(Comparator.comparingInt(Transaction::amount));
        System.out.println("Stable sort (equal amounts):");
        txns.forEach(t -> System.out.println("  " + t.id() + " " + t.date()));
    }
}
TEXT
Stable sort (equal amounts):
  T1 Jan-10
  T2 Jan-12
  T3 Jan-15

How do sorting algorithms compare?

| Algorithm | Time (avg) | Time (worst) | Space | Stable | In-place |
|---|---|---|---|---|---|
| Bubble sort | O(n^2) | O(n^2) | O(1) | Yes | Yes |
| Selection sort | O(n^2) | O(n^2) | O(1) | No | Yes |
| Insertion sort | O(n^2) | O(n^2) | O(1) | Yes | Yes |
| Merge sort | O(n log n) | O(n log n) | O(n) | Yes | No |
| Quick sort | O(n log n) | O(n^2) | O(log n) | No | Yes |
| Heap sort | O(n log n) | O(n log n) | O(1) | No | Yes |
| Counting sort | O(n + k) | O(n + k) | O(k) | Yes | No |
| Radix sort | O(d * n) | O(d * n) | O(n + k) | Yes | No |

JAVA
package com.bank.sort;

import java.util.Arrays;

public class SortComparison {
    public static void main(String[] args) {
        int n = 100_000;
        int[] data = new int[n];
        for (int i = 0; i < n; i++) data[i] = (int)(Math.random() * 1_000_000);

        int[] copy = data.clone();
        long start = System.nanoTime();
        Arrays.sort(copy); // dual-pivot quicksort for primitives
        long elapsed = System.nanoTime() - start;
        System.out.println("Arrays.sort on " + n + " ints: " + elapsed / 1_000_000 + " ms");
    }
}
TEXT
Arrays.sort on 100000 ints: 8 ms

What is the O(n log n) lower bound?

Any comparison-based sorting algorithm must make at least O(n log n) comparisons in the worst case. This is a mathematical fact, not a limitation of specific algorithms. The argument is that with n elements there are n! possible orderings, and each comparison splits the decision tree in two, requiring at least log2(n!) comparisons, which is Theta(n log n).

Non-comparison sorts like counting sort bypass this by using the values directly as indices rather than comparing pairs.

JAVA
package com.bank.sort;

public class LowerBound {
    public static void main(String[] args) {
        for (int n : new int[]{10, 100, 1000, 1_000_000}) {
            double nlogn = n * (Math.log(n) / Math.log(2));
            System.out.printf("n=%-10d  n*log2(n)=%.0f%n", n, nlogn);
        }
    }
}
TEXT
n=10          n*log2(n)=33
n=100         n*log2(n)=664
n=1000        n*log2(n)=9966
n=1000000     n*log2(n)=19931569

How does Java handle sorting internally?

Java's Arrays.sort uses a dual-pivot quicksort for primitive arrays (fast, in-place, but unstable). For object arrays and Collections.sort, it uses TimSort, a stable hybrid of merge sort and insertion sort that exploits existing sorted runs in the data.

JAVA
package com.bank.sort;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

public class JavaSortInternals {
    public static void main(String[] args) {
        // Primitive: dual-pivot quicksort (unstable)
        int[] primitives = {50, 20, 80, 10, 40};
        Arrays.sort(primitives);
        System.out.println("Primitives: " + Arrays.toString(primitives));

        // Objects: TimSort (stable)
        List<String> names = new ArrayList<>(List.of("Rajesh", "Priya", "Pankaj"));
        Collections.sort(names);
        System.out.println("Objects: " + names);
    }
}
TEXT
Primitives: [10, 20, 40, 50, 80]
Objects: [Pankaj, Priya, Rajesh]

What is counting sort and when does it beat comparison sorts?

Counting sort works when the values fall in a known, limited range. It counts occurrences of each value and then reconstructs the sorted output. It runs in O(n + k) time where k is the range, bypassing the O(n log n) lower bound because it does not compare elements.

JAVA
package com.bank.sort;

import java.util.Arrays;

public class CountingSort {

    public static void countingSort(int[] arr, int maxVal) {
        int[] count = new int[maxVal + 1];
        for (int v : arr) count[v]++;
        int idx = 0;
        for (int v = 0; v <= maxVal; v++) {
            while (count[v]-- > 0) arr[idx++] = v;
        }
    }

    public static void main(String[] args) {
        // Transaction status codes: 0=pending, 1=processing, 2=settled
        int[] statuses = {2, 0, 1, 2, 0, 1, 1, 0, 2};
        countingSort(statuses, 2);
        System.out.println("Sorted statuses: " + Arrays.toString(statuses));
    }
}
TEXT
Sorted statuses: [0, 0, 0, 1, 1, 1, 2, 2, 2]

When should you write your own sort versus using the library?

In production, always use the library sort. Java's Arrays.sort and Collections.sort are highly optimised, well-tested, and handle edge cases. Hand-written sorts are reserved for interviews, custom data structures, or educational purposes.

JAVA
package com.bank.sort;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class ProductionSorting {

    record Transaction(String id, BigDecimal amount) {}

    public static void main(String[] args) {
        List<Transaction> txns = new ArrayList<>(List.of(
            new Transaction("T1", new BigDecimal("50000")),
            new Transaction("T2", new BigDecimal("25000")),
            new Transaction("T3", new BigDecimal("75000"))
        ));

        // Production: use library sort with a Comparator
        txns.sort(Comparator.comparing(Transaction::amount));
        txns.forEach(t -> System.out.println(t.id() + ": " + t.amount()));
    }
}
TEXT
T2: 25000
T1: 50000
T3: 75000

What should a senior engineer know about sorting?

A senior engineer chooses the right tool based on data size, stability needs, memory constraints, and access patterns. They know the O(n log n) lower bound, when non-comparison sorts apply, and why Java uses different algorithms for primitives versus objects. They also know that sorting is often a building block: sorted data enables binary search, merge operations, and efficient deduplication.

Visual Overview

DIAGRAM
  Sorting Algorithm Decision Tree:

  ┌──────────────────────────────────┐
  │ Need to sort data?               │
  └──────────┬───────────────────────┘
             ▼
  ┌──────────────────────────────────┐
  │ Small data (< 50 elements)?      │
  │   YES → Insertion sort           │
  │   NO  → Continue                 │
  └──────────┬───────────────────────┘
             ▼
  ┌──────────────────────────────────┐
  │ Limited value range?             │
  │   YES → Counting / Radix sort   │
  │   NO  → Continue                 │
  └──────────┬───────────────────────┘
             ▼
  ┌──────────────────────────────────┐
  │ Need stability?                  │
  │   YES → Merge sort / TimSort     │
  │   NO  → Quick sort / Heap sort   │
  └──────────────────────────────────┘

  Java's Internal Choices:
  ┌─────────────────┬──────────────────────┐
  │ Arrays.sort(int[])│ Dual-pivot Quicksort │
  │ Arrays.sort(T[])  │ TimSort (stable)     │
  │ Collections.sort()│ TimSort (stable)     │
  └─────────────────┴──────────────────────┘

Key Takeaway

Sorting is the process of arranging elements into a defined order using a comparison rule. Every sorting algorithm is characterised by three properties: time complexity, space complexity, and stability. Stability means equal elements keep their original relative order, which matters in banking when sorting transactions by amount while preserving their chronological order.

In Java, the comparison rule is captured by Comparable for natural order or Comparator for external order. Java uses a stable TimSort for object arrays and Collections.sort, and a dual-pivot quicksort for primitive arrays. The simple quadratic sorts (bubble, selection, insertion) are useful for teaching but too slow for large data, while merge sort, quicksort, and heapsort run in O(n log n), which is the proven lower bound for comparison-based sorts.

When data has special structure like a small integer range, non-comparison sorts such as counting sort or radix sort achieve linear time by bypassing comparisons entirely. In production at the National Bank of India, the highly optimised library sort is always the right choice; hand-written sorts are reserved for interviews or very specialised data structures.

The senior-level insight is that sorting is not just a standalone operation but a building block. Sorted data enables binary search, merge operations, efficient deduplication, and range queries. Choosing the right sort based on data size, stability needs, and memory constraints is what interviewers test.

Remember one sentence: sorting arranges data by a comparison rule, and choosing the right algorithm based on stability, complexity, and data characteristics is the real skill.

---

Share
Use ← → arrow keys to navigate