← Skill tree CS Skill Tree 0 CSCD211

list.sort (and Collections.sort) with a Comparator

Textbook: BJP (Reges and Stepp)

Before You Start

Check each box you can do from memory. A box you cannot check yet is not a problem; it points you to a quick refresher, not a grade.

Not sure? Take the 60-second self-check.

1. In-place vs. copy. Does arrays.sort return a sorted copy or modify the array in place?

Check

It modifies the array in place and returns void. The same contract applies to list sort.

2. Collections.sort. When was Collections.sort(list, cmp) added to Java?

Check

Collections.sort has been in the standard library since Java 2. It predates the modern list.sort(cmp), which was added in Java 8 as a default method on List<E>.

3. Immutable list. List.of(1, 2, 3) returns what kind of list?

Check

An immutable list. Any structural modification (add, remove, set, or sort) throws UnsupportedOperationException.


Try This First

Two calls that sort the same list of Engine objects by manufacturer:

// A (legacy)
Collections.sort(myEngines, new ManufacturerSort());

// B (modern)
myEngines.sort(new ManufacturerSort());

Before reading further: are both correct? Which is preferred for new code?

Check

Both are correct and produce identical results. For new code, myEngines.sort(new ManufacturerSort()) is preferred: it reads “sort the list” directly without needing the Collections utility class.


What You Need To Walk In With

The Insight: list.sort(cmp) is the modern in-place list sort; Collections.sort(list, cmp) is its older equivalent. Both call into the same TimSort algorithm and share the same stability guarantee. The reason two forms exist is history: List.sort was added as a default method in Java 8; before that, Collections.sort was the only way. Modern code prefers the instance method form.

To be fully prepared here, you should be comfortable calling either form on a mutable ArrayList, predicting that the original list is modified in place and nothing is returned. You should also know that List.of(...) is immutable, so sorting it requires wrapping it in a new ArrayList first. And you should be able to trace what happens when you sort through an Arrays.asList(arr) view, since changes propagate back to the original array. You can: call both list.sort(cmp) and Collections.sort(list, cmp) correctly, predict in-place mutation for both, wrap an immutable list before sorting, and pass null as the comparator to sort by natural order without constructing a Comparator.naturalOrder() instance.


How It Works

Two forms, one algorithm

Form Available since Preferred?
Collections.sort(list, cmp) Java 2 No (legacy)
list.sort(cmp) Java 8 Yes (modern)

Both call into TimSort. Both sort in place. Both return void. The modern form calls list.sort(cmp) directly on the list instance; the legacy form goes through the Collections utility class.

The F17 Lab pattern

// F17.Lab6-Basic-inheritance
Collections.sort(myList, new ManufacturerSort());

// F17.Lab8-Full-inheritance
Collections.sort(teams, new TeamPayrollSort());

These are the legacy form, preserved in archived labs. Modernized equivalents:

myList.sort(new ManufacturerSort());
teams.sort(new TeamPayrollSort());

No-comparator forms

Both methods have a no-comparator overload that uses natural order:

Collections.sort(list);       // elements must implement Comparable<E>
list.sort(null);              // null comparator = natural order; elements must implement Comparable<E>

The null argument to list.sort is a valid way to sort by natural order without constructing a Comparator.naturalOrder() instance.

Immutable lists and sort

List.of(...) (Java 9+) and other factory-produced lists are immutable. Calling .sort on them throws UnsupportedOperationException:

List<Integer> fixed = List.of(3, 1, 2);
fixed.sort(Integer::compare);   // UnsupportedOperationException at runtime

To sort, wrap in a mutable list first:

List<Integer> mutable = new ArrayList<>(List.of(3, 1, 2));
mutable.sort(Integer::compare);
// mutable is now [1, 2, 3]

The Arrays.asList edge case

Arrays.asList(arr) returns a fixed-size list backed by the array. .sort works on it (mutations propagate to the array), but .add and .remove throw UnsupportedOperationException. Students are sometimes surprised that sorting through the list view also sorts the original array:

String[] arr = {"banana", "apple", "cherry"};
List<String> view = Arrays.asList(arr);
view.sort(String::compareTo);
System.out.println(Arrays.toString(arr));  // [apple, banana, cherry]

arr is sorted even though the sort call was on view. The backing array is the same object.


Worked Example: Predict Then Check

ArrayList<String> words = new ArrayList<>(List.of("banana", "apple", "cherry"));
words.sort(String::compareTo);
System.out.println(words);

List<String> fixed = List.of("banana", "apple", "cherry");
fixed.sort(String::compareTo);

Predict: (a) what words contains after the sort, (b) what happens on the last line.

Reasoning

words is an ArrayList (mutable). sort sorts it in place. Result: [apple, banana, cherry].

fixed is a List.of list (immutable). sort attempts mutation. Result: UnsupportedOperationException.

Show answer

(a) words is [apple, banana, cherry].

(b) fixed.sort(...) throws UnsupportedOperationException at runtime. The list is immutable.


Quick check

Check your understanding

After calling myEngines.sort(new ManufacturerSort()), which of the following is true?

Tier 1 · BJP (Reges and Stepp), Ch 10


Common Misconceptions

Misconception 1: sorting through an Arrays.asList view does not affect the original array

Wrong mental model: “The list is a separate object from the array, so sorting the list doesn’t touch the array.”

Why it breaks: Arrays.asList(arr) returns a view backed by the array. Sorting the list reorders the array elements directly:

String[] arr = {"b", "a", "c"};
Arrays.asList(arr).sort(String::compareTo);
System.out.println(arr[0]);  // "a" (the array was sorted)

How to correct: If you want the array sorted, sorting via the view is correct. If you want a sorted copy, use new ArrayList<>(Arrays.asList(arr)) and sort the copy.


Misconception 2: sorting an immutable list with list.sort

Wrong mental model:List.of lists are in the List interface; List.sort should work on them.”

Why it breaks: List.of produces an immutable list whose sort method delegates to the default implementation, which calls replaceAll or set internally. Both are blocked by the immutability, causing UnsupportedOperationException.

How to correct: Wrap first: new ArrayList<>(immutableList).sort(cmp).


Quick check

Check your understanding

A student writes the following and expects the array to remain unchanged: String[] arr = {“c”, “a”, “b”}; Arrays.asList(arr).sort(String::compareTo); System.out.println(arr[0]); What is printed?

Tier 2 · BJP (Reges and Stepp), Ch 10


Formal Definition and Interface Contract

From the Java 25 List.sort API:

default void sort(Comparator<? super E> c)

Sorts this list according to the order induced by the specified Comparator. The sort is stable: this method must not reorder equal elements. The list must be modifiable, but need not be resizable. If the list’s ListIterator does not support the set operation then an UnsupportedOperationException is thrown when sorting a non-empty list.

From the Java 25 Collections.sort API:

public static <T> void sort(List<T> list, Comparator<? super T> c)

Sorts the specified list according to the order induced by the specified comparator. All elements in the list must be mutually comparable using the specified comparator. The sort is stable and guaranteed to be n log(n).


Mental Model

list.sort(cmp) is the same operation as Arrays.sort(arr, cmp), applied to a list. Both rearrange their container in place. The list equivalent of “clone before sort” is new ArrayList<>(original) to make a mutable copy. The list equivalent of Arrays.asList(arr) being a backed view is that sorting that view also sorts the backing array.


Connections

Within CSCD 210/211: F17 Labs 6 and 8 use Collections.sort on ArrayList instances. This page explains that form and shows its modern equivalent. ArrayList is covered in depth with the collections material.

Looking back: Arrays.sort with a Comparator covered the array form. Both share the same in-place, void-return contract and TimSort algorithm.

Looking ahead: Stable Sort and the Two-Pass Trick covers how stability enables multi-key sorting by sorting on the secondary key first and the primary key second.


Go Deeper (optional)

None of what follows is needed to write correct sorting code. It is here for the curious, and you are under no pressure to read it now.

The two-form history of list sorting reflects a recurring tension in library design: adding useful new methods to a widely-implemented interface without forcing every existing implementation to update. Before Java 8, adding an abstract method to List would have broken every class in the ecosystem that implemented List. The solution was the default method mechanism, introduced in Java 8, which provides a fallback body right on the interface. List.sort is one of the clearest examples of interface evolution without breaking compatibility: every pre-existing ArrayList, LinkedList, or custom List implementation got list.sort(cmp) for free when Java 8 shipped, even without a code change. This is a real design-pattern story about how a language feature (default methods) was added to Java specifically to allow the Collections API to be modernized safely.

The career relevance is direct: codebases that started before Java 8 (released March 2014) use Collections.sort throughout their sorting code. Recognizing both forms and knowing they produce identical results is an everyday skill for any developer reading, reviewing, or modernizing production Java code. The list.sort(cmp) instance-method form also reads more naturally in method chains and modern Stream-adjacent patterns, which is why code style guides in industry now uniformly prefer it.

One small idiom worth keeping in your toolkit: list.sort(null) is the compact way to sort a list by natural order when you know the elements implement Comparable. It avoids constructing a Comparator.naturalOrder() instance and is slightly shorter at the call site. The Java specification explicitly defines null as meaning natural order here, so it is not a workaround but an intended usage.


Practice

Level 1

Rewrite Collections.sort(teams, new TeamPayrollSort()); in the modern form.

Show answer
teams.sort(new TeamPayrollSort());

Source: modernization of F17.Lab8-Full-inheritance pattern.


Level 2

A student writes:

List<String> words = List.of("banana", "apple", "cherry");
words.sort(Comparator.naturalOrder());

Identify the runtime error and provide a fix.

Show answer

Error: UnsupportedOperationException at runtime. List.of returns an immutable list; sort requires mutation.

Fix:

List<String> words = new ArrayList<>(List.of("banana", "apple", "cherry"));
words.sort(Comparator.naturalOrder());

new ArrayList<>(...) creates a mutable copy that can be sorted.


Level 3

Explain the relationship between Arrays.asList(arr).sort(cmp) and Arrays.sort(arr, cmp). Are they equivalent? Write code that demonstrates the backing relationship.

Show answer

Equivalence: Both sort the same underlying array. Arrays.asList(arr) returns a list view backed by arr. Sorting the view reorders the array; sorting the array directly also reorders the view. The outcomes are equivalent.

Demonstration:

String[] arr = {"c", "a", "b"};
List<String> view = Arrays.asList(arr);

// Sort through the view
view.sort(String::compareTo);

// The original array is sorted too
System.out.println(Arrays.toString(arr));  // [a, b, c]
System.out.println(view);                  // [a, b, c]

The backing relationship means view.get(0) and arr[0] always refer to the same element. Reordering through either handle reorders through both.


Check Yourself

Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.

Check your understanding

Which form of list sorting is preferred for new Java code written after Java 8?

Tier 1 · BJP (Reges and Stepp), Ch 10

What does list.sort(cmp) return?

Tier 1 · BJP (Reges and Stepp), Ch 10

A student calls List.of(“c”, “a”, “b”).sort(String::compareTo). What happens at runtime?

Tier 2 · BJP (Reges and Stepp), Ch 10

Trace this code and select the correct output printed to the console: String[] arr = {“banana”, “apple”, “cherry”}; List<String> view = Arrays.asList(arr); view.sort(String::compareTo); System.out.println(arr[0]);

Tier 2 · BJP (Reges and Stepp), Ch 10

What argument do you pass to list.sort to sort a list of Comparable elements in natural order, without constructing a Comparator instance?

Tier 3 · BJP (Reges and Stepp), Ch 10