JAVA • ALGORITHMS AND COMPLEXITY

Arrays.sort & Collections.sort — Use Arrays.sort and Collections.sort; compare comparators (intro)

Master Java's built-in sorting tools and learn how comparators let you control sort order.

Historical Context & Motivation

Sorting is one of the oldest and most studied problems in computer science. Even before modern computers existed, people needed efficient ways to organize information — think of libraries alphabetizing their card catalogs or census offices ordering population records. When programming languages matured, developers realized they shouldn't have to write their own sorting code from scratch every time. This led to the creation of standard library sorting methods that are fast, reliable, and easy to use.

Java's sorting utilities evolved alongside the language itself. The earliest versions shipped with basic sorting routines, but over the years the Java team refined these tools to handle arrays, lists, and custom ordering rules. Understanding this history helps you appreciate why Java provides both Arrays.sort and Collections.sort — and why comparators became a central part of the design.

1996
Java 1.0 Launches
Java ships with java.util.Arrays providing basic sorting for primitive arrays. The underlying algorithm is a variant of quicksort.
1998
Java Collections Framework (JCF)
Java 1.2 introduces the Collections Framework, adding Collections.sort and the Comparator interface for custom ordering of object lists.
2009
TimSort Adopted
Java 7 replaces the old merge sort for objects with TimSort, a hybrid algorithm that performs excellently on real-world data with partially sorted runs.
2011
Dual-Pivot Quicksort
Java 7 also introduces Dual-Pivot Quicksort by Vladimir Yaroslavskiy for primitive arrays, outperforming the classic single-pivot version in practice.
2014
Lambda Expressions
Java 8 adds lambda syntax, letting you write comparators in a single line like (a, b) -> a.length() - b.length(), dramatically reducing boilerplate code.

The key question this lesson addresses is: how do you sort data in Java efficiently, and how do you customize the sort order when the default (ascending) isn't what you need? The answer lies in mastering Arrays.sort, Collections.sort, and the Comparator interface.

Core Principles & Definitions

Before diving into code, you need to understand a few foundational ideas. Java provides two main entry points for sorting — one for raw arrays and one for list-based collections. Both share the concept of a natural ordering (the default way elements compare themselves) and a custom ordering defined by a comparator you write.

1

Arrays.sort

A static method in java.util.Arrays that sorts arrays in place. Works on both primitive arrays (int[], double[]) and object arrays (String[], Integer[]). Uses Dual-Pivot Quicksort for primitives and TimSort for objects.
2

Collections.sort

A static method in java.util.Collections that sorts List objects (like ArrayList or LinkedList). Under the hood, it converts the list to an array, uses TimSort, then writes the sorted result back.
3

Comparable Interface

A class implements Comparable<T> to define its natural ordering. The compareTo() method returns a negative, zero, or positive int to indicate order. Strings and Integers already implement this.
4

Comparator Interface

A separate object that defines a compare(a, b) method for custom ordering. You can pass a Comparator to both Arrays.sort and Collections.sort to override natural ordering — sorting by length, reverse order, or any rule you invent.
5

Stability

A sort is stable if equal elements keep their original relative order. TimSort (used for objects) is stable. Dual-Pivot Quicksort (used for primitives) is not guaranteed stable, but this only matters when elements are distinguishable beyond their sort key.
✦ KEY TAKEAWAY
Think of sorting like organizing a playlist. The natural ordering is alphabetical by song title — that's the default. A comparator is like telling the music app: "Actually, sort by artist, then by year." You're providing a custom rule that overrides the default. Arrays.sort works on raw arrays (the plain array in memory), while Collections.sort works on Lists (a higher-level container).

Visual Explanation — How Sorting Flows

The following diagram shows the decision tree when you want to sort data in Java. It starts with the type of collection you have (an array or a List) and follows through to how a Comparator plugs in to change the sort order.

This flowchart shows the decision path: start with your data type (array or List), choose the appropriate sort method, and optionally pass a Comparator to customize ordering. Both paths modify the original data in place.

Notice that both Arrays.sort and Collections.sort converge on the same outcome: your data ends up rearranged in the original container. Neither method returns a new array or list — they sort in place. The critical fork is whether you supply a Comparator. Without one, Java falls back to natural ordering (numbers ascending, strings alphabetically). With one, you take full control of the sort logic.

How Comparators Work

A Comparator is an object that implements the java.util.Comparator<T> interface. It has one essential method: int compare(T a, T b). The return value tells the sort algorithm how to order the two elements.

COMPARATOR CONTRACT
compare(a, b) → negative ⇒ a comes before b compare(a, b) → 0 ⇒ a and b are equal compare(a, b) → positive ⇒ a comes after b
The magnitude of the return value doesn't matter — only its sign (negative, zero, or positive) determines ordering. For example, returning −1 or −1000 both mean "a comes first."

Three Ways to Create a Comparator

There are three common approaches. The first is an anonymous inner class, where you create the Comparator object inline without naming a separate class. This was the standard approach before Java 8:

💻 Anonymous Inner Class
Arrays.sort(words, new Comparator<String>() { public int compare(String a, String b) { return a.length() - b.length(); } }); This sorts the words array by string length (shortest first).

The second approach uses a lambda expression (Java 8+), which is much shorter and cleaner. Since Comparator has exactly one abstract method, Java lets you replace the whole anonymous class with an arrow function:

⚡ Lambda Expression
Arrays.sort(words, (a, b) -> a.length() - b.length()); This single line does exactly the same thing as the anonymous inner class above.

The third approach uses the Comparator utility methods introduced in Java 8, such as Comparator.comparingInt():

✨ Utility Method
Arrays.sort(words, Comparator.comparingInt(String::length)); This reads almost like English: "sort words, comparing by integer result of length." It's the most readable option for simple key-based sorts.
REVERSE ORDER SHORTCUT
Collections.sort(list, Comparator.reverseOrder()) Collections.sort(list, myComparator.reversed())
Use Comparator.reverseOrder() to flip natural ordering, or call .reversed() on any existing comparator to invert its logic.

Arrays.sort vs. Collections.sort — Side by Side

While both methods sort data, they target different data structures and have subtle differences. The diagram below visualizes what happens under the hood for each method, and the table that follows provides a feature-by-feature comparison.

Left: Arrays.sort directly sorts the array in place using Dual-Pivot Quicksort (for primitives) or TimSort (for objects). Right: Collections.sort first converts the list to an array, applies TimSort, then copies sorted elements back into the list.
Feature comparison of Arrays.sort and Collections.sort
FeatureArrays.sortCollections.sort
Works onArrays (int[], String[], etc.)Lists (ArrayList, LinkedList)
Primitives?Yes — int[], double[], char[], etc.No — Lists cannot hold primitives directly; use wrapper classes (Integer, Double)
Algorithm (objects)TimSort — O(n log n)TimSort — O(n log n)
Algorithm (primitives)Dual-Pivot Quicksort — O(n log n) averageN/A
Stable?Objects: yes. Primitives: not guaranteed.Yes (always TimSort)
Comparator supportYes (for object arrays only)Yes
Import neededimport java.util.Arrays;import java.util.Collections;

Worked Example — Sorting Student Names

Let's walk through a complete example. Imagine you have an ArrayList of student names and you want to sort them first alphabetically (natural order), and then by name length (custom order using a comparator).

Sorting an ArrayList Two Ways
1
Step 1 — Create the ListWe start by creating an ArrayList and adding five student names: ArrayList<String> names = new ArrayList<>(); names.add("Zara"); names.add("Bo"); names.add("Michaela"); names.add("Lee"); names.add("Alejandro"); At this point, the list order is: [Zara, Bo, Michaela, Lee, Alejandro].
List: [Zara, Bo, Michaela, Lee, Alejandro]
2
Step 2 — Sort Alphabetically (Natural Order)Calling Collections.sort(names) uses String's natural ordering, which is alphabetical (case-sensitive, uppercase before lowercase). Since all our names start with uppercase letters followed by lowercase, this works as expected.
Sorted: [Alejandro, Bo, Lee, Michaela, Zara]
3
Step 3 — Define a Length ComparatorNow we want to sort by name length (shortest first). We write a lambda comparator: Comparator<String> byLength = (a, b) -> a.length() - b.length(); When a is shorter than b, a.length() - b.length() returns a negative number, meaning a comes first. That's exactly what we want.
Comparator defined: shortest name first
4
Step 4 — Sort with the ComparatorWe pass the comparator as the second argument: Collections.sort(names, byLength); The sort algorithm now uses our byLength comparator instead of alphabetical order. Names with 2 letters come before names with 3 letters, and so on.
Sorted by length: [Bo, Lee, Zara, Michaela, Alejandro]
5
Step 5 — Reverse the Custom OrderTo sort longest first, we reverse the comparator: Collections.sort(names, byLength.reversed()); The .reversed() method returns a new comparator that flips the ordering.
Final result: [Alejandro, Michaela, Zara, Lee, Bo]
⚠️ Important Note
Be careful with the subtraction trick a.length() - b.length() when comparing large integers — it can overflow! For lengths of strings this is safe, but for comparing arbitrary int values, prefer Integer.compare(a, b) instead.

Strengths, Limitations & When to Use Each

Both sorting methods are powerful, but each has trade-offs depending on your situation. The table below summarizes when you'd reach for one versus the other, and when a comparator is necessary.

Choosing between Arrays.sort and Collections.sort
ScenarioBest ChoiceWhy
Sorting an int[] ascendingArrays.sort(arr)Primitives can only be sorted with Arrays.sort. Fast Dual-Pivot Quicksort is used.
Sorting an ArrayList<String>Collections.sort(list)Lists require Collections.sort (or list.sort()). Natural ordering gives alphabetical.
Sorting an int[] descendingSort ascending, then reverse manually (or use Integer[])Arrays.sort on primitives doesn't accept a Comparator. You must use a wrapper type or reverse after sorting.
Sorting objects by a custom fieldEither method + ComparatorPass a Comparator lambda to control which field(s) determine order.
Need stable sort (equal items keep original order)Either method with objectsTimSort (used for objects) is stable. Primitive sort is not guaranteed stable.
✦ KEY TAKEAWAY
Think of it like choosing a tool for a job. Arrays.sort is like a power drill — fast and direct, designed for raw materials (arrays). Collections.sort is like a drill with interchangeable heads — it works through an extra layer (the List interface) but is just as effective. A Comparator is the drill bit you swap in to change what kind of hole you're making — it doesn't change the drill, just the result.
⚠️ Limitation: Primitive Arrays & Comparators
A common gotcha: you cannot pass a Comparator to Arrays.sort(int[]). Java doesn't support generics with primitive types, so to sort an int array in a custom order you must first convert it to an Integer[] or use an ArrayList<Integer>.

Connection to Advanced Sorting Concepts

The sorting tools you've learned are your first step into a much larger landscape. As you advance in computer science, you'll encounter situations where built-in sort methods aren't enough — or where understanding the underlying algorithms becomes critical for performance analysis.

From introductory sorting to advanced concepts
What You Know NowWhat Comes Next
Arrays.sort / Collections.sortImplementing your own sorting algorithms (merge sort, quicksort, heap sort) to understand time and space complexity trade-offs.
Comparator with lambda expressionsChaining comparators with .thenComparing() for multi-level sorts (e.g., sort by last name, then by first name).
O(n log n) average performanceBig-O analysis of best, average, and worst cases. Understanding why O(n log n) is the theoretical lower bound for comparison-based sorting.
Stable vs. unstable sortRadix sort and counting sort — non-comparison algorithms that can beat O(n log n) for specific data types.
Sorting lists and arraysUsing Stream.sorted() for functional-style sorting that returns new collections without modifying originals.

As you move into AP Computer Science or college-level courses, you'll be expected to analyze sorting algorithms in terms of time complexity and space complexity. For now, the important takeaway is that Java's built-in sort methods use highly optimized O(n log n) algorithms, and you should always prefer them over writing your own sort — unless an assignment specifically asks you to implement one.

Practice Problems

PROBLEM 1 — CONCEPTUAL
What is the difference between Comparable and Comparator? When would you use each one?
PROBLEM 2 — BASIC CALCULATION
Given the array int[] nums = {8, 3, 12, 1, 5};, write one line of code to sort it in ascending order. What will the array contain after sorting?
PROBLEM 3 — INTERMEDIATE
You have ArrayList<String> fruits = new ArrayList<>(Arrays.asList("banana", "fig", "apple", "kiwi", "elderberry"));. Write code to sort this list by string length (shortest first). If two fruits have the same length, they should remain in their original relative order.
PROBLEM 4 — APPLIED
A teacher stores student scores in Integer[] scores = {88, 72, 95, 88, 65};. She wants to display them from highest to lowest. Write the code. Then explain: why do we need Integer[] instead of int[] to use a Comparator?
PROBLEM 5 — CRITICAL THINKING
Consider a list of words: ["hi", "zoo", "an", "be"]. You sort it with Collections.sort(words) (natural order) and then separately sort the original list with a length-based comparator. The natural-order result is [an, be, hi, zoo]. The length-based result is [hi, an, be, zoo]. Both "hi", "an", and "be" have length 2. Explain why the two-letter words appear in different orders in each result, and connect this to the concept of stability.

Lesson Summary

Java provides two primary sorting tools: Arrays.sort for arrays and Collections.sort for Lists. Both sort data in place using efficient O(n log n) algorithms. For primitive arrays, Java uses Dual-Pivot Quicksort; for objects (in both methods), it uses TimSort, which is a stable sorting algorithm.

By default, elements are sorted in natural order (ascending for numbers, alphabetical for strings). To customize the sort, you supply a Comparator — a separate object whose compare(a, b) method returns a negative, zero, or positive integer to define order. Lambda expressions (Java 8+) make comparators concise: (a, b) -> a.length() - b.length(). Remember that primitive arrays cannot use Comparators — you must use wrapper types like Integer[] for custom ordering of numeric data.

Varsity Tutors • Java • Arrays.sort & Collections.sort — Use Arrays.sort and Collections.sort; compare comparators (intro)