Java Sorting Algorithms Guide: Merge Sort, Quick Sort, Heap Sort, TimSort, Radix Sort (2025)


Java Sorting Algorithms

Sorting algorithms are fundamental to computer science and software development. This comprehensive guide explores five essential sorting algorithms in Java, their implementations, performance characteristics, and use cases.

Pro Tip: Choosing the right sorting algorithm depends on your specific requirements for stability, space complexity, and performance characteristics.

Introduction to Sorting Algorithms

Note: Understanding the characteristics of each sorting algorithm is crucial for making the right choice in your applications.

Sorting algorithms can be categorized based on several characteristics:

  • Stability: Whether equal elements maintain their relative order
  • In-place: Whether the algorithm requires additional memory
  • Time Complexity: Best, average, and worst-case performance
  • Space Complexity: Additional memory requirements

Merge Sort

Pro Tip: Merge Sort is an excellent choice when you need a stable sort with guaranteed O(n log n) performance.

Characteristics

  • Time Complexity: O(n log n) always
  • Stable: Yes
  • In-place: No
  • Space Complexity: O(n)

Implementation


public class MergeSort {
    public static void sort(int[] arr) {
        if (arr.length < 2) return;
        
        int mid = arr.length / 2;
        int[] left = new int[mid];
        int[] right = new int[arr.length - mid];
        
        System.arraycopy(arr, 0, left, 0, mid);
        System.arraycopy(arr, mid, right, 0, arr.length - mid);
        
        sort(left);
        sort(right);
        merge(arr, left, right);
    }
    
    private static void merge(int[] arr, int[] left, int[] right) {
        int i = 0, j = 0, k = 0;
        
        while (i < left.length && j < right.length) {
            if (left[i] <= right[j]) {
                arr[k++] = left[i++];
            } else {
                arr[k++] = right[j++];
            }
        }
        
        while (i < left.length) {
            arr[k++] = left[i++];
        }
        
        while (j < right.length) {
            arr[k++] = right[j++];
        }
    }
}

Use Cases

  • Large datasets where stability is required
  • External sorting (sorting data that doesn't fit in memory)
  • When consistent O(n log n) performance is needed

Quick Sort

Note: Quick Sort is generally faster than Merge Sort in practice, but it's not stable and has a worst-case O(n²) time complexity.

Characteristics

  • Time Complexity: Best/Average: O(n log n), Worst: O(n²)
  • Stable: No
  • In-place: Yes
  • Space Complexity: O(log n) for recursion stack

Implementation


public class QuickSort {
    public static void sort(int[] arr) {
        sort(arr, 0, arr.length - 1);
    }
    
    private static void sort(int[] arr, int low, int high) {
        if (low < high) {
            int pi = partition(arr, low, high);
            sort(arr, low, pi - 1);
            sort(arr, pi + 1, high);
        }
    }
    
    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                swap(arr, i, j);
            }
        }
        
        swap(arr, i + 1, high);
        return i + 1;
    }
    
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

Use Cases

  • General-purpose sorting when stability is not required
  • When average-case performance is more important than worst-case
  • In-memory sorting of large datasets

Heap Sort

Pro Tip: Heap Sort is an excellent choice when you need an in-place sort with guaranteed O(n log n) performance.

Characteristics

  • Time Complexity: O(n log n)
  • Stable: No
  • In-place: Yes
  • Space Complexity: O(1)

Implementation


public class HeapSort {
    public static void sort(int[] arr) {
        int n = arr.length;
        
        // Build max heap
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }
        
        // Extract elements from heap
        for (int i = n - 1; i > 0; i--) {
            swap(arr, 0, i);
            heapify(arr, i, 0);
        }
    }
    
    private static void heapify(int[] arr, int n, int i) {
        int largest = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        
        if (left < n && arr[left] > arr[largest]) {
            largest = left;
        }
        
        if (right < n && arr[right] > arr[largest]) {
            largest = right;
        }
        
        if (largest != i) {
            swap(arr, i, largest);
            heapify(arr, n, largest);
        }
    }
    
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

Use Cases

  • When memory usage is a concern
  • When guaranteed O(n log n) performance is needed
  • When stability is not required

TimSort

Note: TimSort is the default sorting algorithm in Java's Arrays.sort() and Collections.sort() methods.

Characteristics

  • Time Complexity: Best: O(n), Average/Worst: O(n log n)
  • Stable: Yes
  • In-place: Yes
  • Space Complexity: O(n)

Implementation


// TimSort is complex to implement from scratch
// Here's how to use Java's built-in TimSort
import java.util.Arrays;

public class TimSortExample {
    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 5, 6};
        
        // Using Arrays.sort() which implements TimSort
        Arrays.sort(arr);
        
        // For objects, Collections.sort() uses TimSort
        List list = Arrays.asList(5, 2, 9, 1, 5, 6);
        Collections.sort(list);
    }
}

Use Cases

  • Default choice for most sorting needs in Java
  • Real-world data that often has some pre-existing order
  • When stability is required

Radix Sort

Pro Tip: Radix Sort is particularly efficient for sorting integers or strings with fixed-length keys.

Characteristics

  • Time Complexity: O(nk) where k is the number of digits
  • Stable: Yes
  • In-place: Yes
  • Space Complexity: O(n + k)

Implementation


public class RadixSort {
    public static void sort(int[] arr) {
        int max = getMax(arr);
        
        for (int exp = 1; max / exp > 0; exp *= 10) {
            countSort(arr, exp);
        }
    }
    
    private static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }
    
    private static void countSort(int[] arr, int exp) {
        int[] output = new int[arr.length];
        int[] count = new int[10];
        
        for (int i = 0; i < arr.length; i++) {
            count[(arr[i] / exp) % 10]++;
        }
        
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }
        
        for (int i = arr.length - 1; i >= 0; i--) {
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];
            count[(arr[i] / exp) % 10]--;
        }
        
        System.arraycopy(output, 0, arr, 0, arr.length);
    }
}

Use Cases

  • Sorting integers with a limited range
  • Sorting strings with fixed-length keys
  • When the number of digits is small compared to the number of elements

Algorithm Comparison

Pro Tip: Choose the sorting algorithm based on your specific requirements for stability, space complexity, and performance characteristics.
Algorithm Best Case Average Case Worst Case Stable In-place
Merge Sort O(n log n) O(n log n) O(n log n) Yes No
Quick Sort O(n log n) O(n log n) O(n²) No Yes
Heap Sort O(n log n) O(n log n) O(n log n) No Yes
TimSort O(n) O(n log n) O(n log n) Yes Yes
Radix Sort O(nk) O(nk) O(nk) Yes Yes

Best Practices

Note: Always consider your specific requirements before choosing a sorting algorithm.

When to Use Each Algorithm

  • Merge Sort: When stability and guaranteed O(n log n) performance are required
  • Quick Sort: For general-purpose sorting when stability isn't required
  • Heap Sort: When memory usage is a concern and stability isn't required
  • TimSort: Default choice for most sorting needs in Java
  • Radix Sort: For sorting integers or strings with fixed-length keys

Performance Considerations

  • Consider the size of your dataset
  • Evaluate memory constraints
  • Check if stability is required
  • Consider the nature of your data (pre-sorted, random, etc.)
  • Profile your application to identify bottlenecks

Conclusion

Understanding the characteristics and implementations of different sorting algorithms is crucial for making informed decisions in your Java applications. Each algorithm has its strengths and weaknesses, and the best choice depends on your specific requirements for stability, space complexity, and performance characteristics.