Java · Course navigation

Java sorting: insertion sort, merge sort, and comparators

Implement two sorts by hand and sort objects with a Comparator.

On this page
Before you start

Java arrays, recursion, and lambdas.

  • Implement two sorts by hand and sort objects with a Comparator.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

Insertion sort grows a sorted prefix: it takes the next element and shifts larger elements to the right until it finds the right place. It is simple and fast for short or nearly sorted input but takes O(n²) in the worst case. Merge sort splits the array in half, sorts each half recursively, and merges the two sorted halves in linear time, giving O(n log n) always, at the cost of O(n) extra space.

In real programs use Arrays.sort or List.sort. For objects they use a stable merge-based sort (TimSort), which keeps equal elements in their original order. A Comparator describes the order, and Comparator.comparing builds one from a key. Here strings are sorted by length first and then alphabetically.

Code example

Java
import java.util.*;

class Main {
  static void insertionSort(int[] a) {
    for (int i = 1; i < a.length; i++) {
      int key = a[i];
      int j = i - 1;
      while (j >= 0 && a[j] > key) {
        a[j + 1] = a[j];
        j--;
      }
      a[j + 1] = key;
    }
  }

  static int[] mergeSort(int[] a) {
    if (a.length <= 1) {
      return a;
    }
    int[] left = mergeSort(Arrays.copyOfRange(a, 0, a.length / 2));
    int[] right = mergeSort(Arrays.copyOfRange(a, a.length / 2, a.length));
    int[] merged = new int[a.length];
    int i = 0, j = 0, k = 0;
    while (i < left.length && j < right.length) {
      merged[k++] = left[i] <= right[j] ? left[i++] : right[j++];
    }
    while (i < left.length) {
      merged[k++] = left[i++];
    }
    while (j < right.length) {
      merged[k++] = right[j++];
    }
    return merged;
  }

  public static void main(String[] args) {
    int[] data = {5, 2, 9, 1, 5, 6};
    int[] copy = data.clone();
    insertionSort(copy);
    System.out.println(Arrays.toString(copy));
    System.out.println(Arrays.toString(mergeSort(data)));

    List<String> words = new ArrayList<>(List.of("pear", "fig", "apple", "kiwi", "plum"));
    words.sort(Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder()));
    System.out.println(words);
  }
}
Output
[1, 2, 5, 5, 6, 9]
[1, 2, 5, 5, 6, 9]
[fig, kiwi, pear, plum, apple]

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1import java.util.*;
2
3class Main {
4 static void insertionSort(int[] a) {
5 for (int i = 1; i < a.length; i++) {
6 int key = a[i];
7 int j = i - 1;
8 while (j >= 0 && a[j] > key) {
9 a[j + 1] = a[j];
10 j--;
11 }
12 a[j + 1] = key;
13 }
14 }
15
16 static int[] mergeSort(int[] a) {
17 if (a.length <= 1) {
18 return a;
19 }
20 int[] left = mergeSort(Arrays.copyOfRange(a, 0, a.length / 2));
21 int[] right = mergeSort(Arrays.copyOfRange(a, a.length / 2, a.length));
22 int[] merged = new int[a.length];
23 int i = 0, j = 0, k = 0;
24 while (i < left.length && j < right.length) {
25 merged[k++] = left[i] <= right[j] ? left[i++] : right[j++];
26 }
27 while (i < left.length) {
28 merged[k++] = left[i++];
29 }
30 while (j < right.length) {
31 merged[k++] = right[j++];
32 }
33 return merged;
34 }
35
36 public static void main(String[] args) {
37 int[] data = {5, 2, 9, 1, 5, 6};
38 int[] copy = data.clone();
39 insertionSort(copy);
40 System.out.println(Arrays.toString(copy));
41 System.out.println(Arrays.toString(mergeSort(data)));
42
43 List<String> words = new ArrayList<>(List.of("pear", "fig", "apple", "kiwi", "plum"));
44 words.sort(Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder()));
45 System.out.println(words);
46 }
47}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Shift larger elements one place to the right to open a gap for the key.—
—
Sort the left half recursively.—
—
Take the smaller front element from the two sorted halves. Using <= keeps equal elements in order, so the sort is stable.—
—
Insertion sort result.—
[1, 2, 5, 5, 6, 9]
Merge sort gives the same result; data itself was not modified.—
[1, 2, 5, 5, 6, 9]
[1, 2, 5, 5, 6, 9]
Sorted by length (3, 4, 4, 4, 5), then alphabetically within the same length.—
[1, 2, 5, 5, 6, 9]
[1, 2, 5, 5, 6, 9]
[fig, kiwi, pear, plum, apple]

Behind the scenes

How Java processes this code

The Java compiler produces bytecode. The JVM loads the class and invokes its main method.

Cost and common mistakes

Insertion sort O(n²) worst case and O(n) best case. Merge sort O(n log n) time and O(n) space.

  • Without the <= in the merge step, equal elements could swap order, and the sort would not be stable.
  • The merge sort here allocates new arrays at every level. A production version reuses one buffer.
  • Comparators must be consistent. Subtracting two ints in compare can overflow, so use Integer.compare.

Try it yourself

Which sort would you use for 10 nearly sorted items, and which for a million random items?

Reveal answer and reasoning

Insertion sort for the small nearly sorted list, because it is close to linear there. Merge sort (or the library's TimSort) for the million items.

Further reading