sort()
Sorts a list in place with a Comparator, stably, and shows why it fails on List.of and returns nothing.
default void sort(Comparator<? super E> c)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$c | Comparator<? super E> | The comparator that defines the order. Pass null to use the elements' natural ordering (they must implement Comparable). |
Return value
voidNothing. The list itself is reordered.
Errors
ClassCastException if c is null and the elements are not mutually Comparable. UnsupportedOperationException if the list is unmodifiable. IllegalArgumentException (optional) if the comparator is found to break its contract.
list.sort(comparator) reorders the list in place. The Comparator decides the order; passing null uses natural ordering, so names.sort(null) sorts strings alphabetically and numbers ascending. It was added in Java 8, and Collections.sort(list) now simply calls it.
It returns void
sort changes the list it is called on and returns nothing. List<String> sorted = names.sort(null); does not compile. If you want a sorted copy and want to leave the original alone, use names.stream().sorted(comparator).toList().
It needs a modifiable list
Lists from List.of, List.copyOf, Stream.toList() and Collections.unmodifiableList throw UnsupportedOperationException. Sort a copy instead. Arrays.asList lists can be sorted, because sorting does not change their size.
Building comparators
Comparator.comparing(Product::name)sorts by a key;comparingIntand friends avoid boxing for primitives..reversed()flips the whole comparator built so far, and.thenComparing(...)breaks ties.Comparator.nullsFirst(...)andnullsLast(...)handle null elements or keys, which otherwise throwNullPointerException.String.CASE_INSENSITIVE_ORDERstops capitals sorting before lower-case letters.
Do not write comparators as a.price() - b.price(): subtraction can overflow for large values and produce the wrong sign. Integer.compare or comparingInt are always correct.
Stable sorting
The sort is stable: elements that compare as equal keep their existing order. That means you can sort by a secondary key first and then by the primary key, although thenComparing is clearer. The ArrayList implementation sorts its array directly in O(n log n).
For data in a database, sort with ORDER BY in the query. Sorting in Java suits small result sets, data merged from several sources, and in-memory test fixtures. Validate any client-supplied sort field against an allow-list before you choose a comparator from it.
Example
When to use it
- Ordering results merged from two services before returning them in one API response.
- Sorting an in-memory list by a client-chosen field from an allow-list of sort keys.
- Putting records in a predictable order before writing a CSV export or comparing in a test.
Frequently asked questions
What is the difference between list.sort and Collections.sort?
None in behaviour. Since Java 8 Collections.sort(list, c) calls list.sort(c). Calling list.sort directly is the modern style.
How do I sort a list in descending order in Java?
Pass a reversed comparator: list.sort(Comparator.reverseOrder()) for natural order, or Comparator.comparing(Item::price).reversed() for a key.
Why does sort throw UnsupportedOperationException?
The list is unmodifiable, for example from List.of or Stream.toList(). Sort a copy with new ArrayList<>(list), or use stream().sorted().toList().
More examples
Sorting an unmodifiable list
Copy into an ArrayList to sort in place, or sort through a stream to get a new list. Either way the original stays untouched.
void main() {
List<String> codes = List.of("FR", "DE", "GB");
try {
codes.sort(null);
} catch (UnsupportedOperationException e) {
IO.println("List.of cannot be sorted in place");
}
List<String> copy = new ArrayList<>(codes);
copy.sort(null);
IO.println(copy);
IO.println(codes.stream().sorted(Comparator.reverseOrder()).toList());
IO.println(codes);
}
// Output:
// List.of cannot be sorted in place
// [DE, FR, GB]
// [GB, FR, DE]
// [FR, DE, GB]Null keys and case
nullsLast stops the null city from throwing and places it at the end. CASE_INSENSITIVE_ORDER sorts Berlin before london, which natural order would not.
record Customer(String name, String city) {}
void main() {
List<Customer> customers = new ArrayList<>(List.of(
new Customer("Ada", "london"),
new Customer("Linus", null),
new Customer("Grace", "Berlin")));
customers.sort(Comparator.comparing(Customer::city,
Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER)));
customers.forEach(c -> IO.println(c.name() + " " + c.city()));
}
// Output:
// Grace Berlin
// Ada london
// Linus nullOverflow-safe comparison
MAX_VALUE minus -10 overflows to a negative number, so the subtraction comparator puts the larger balance first. comparingInt uses Integer.compare and gets it right.
record Account(String id, int balance) {}
void main() {
List<Account> accounts = new ArrayList<>(List.of(
new Account("a", Integer.MAX_VALUE),
new Account("b", -10)));
List<Account> wrong = new ArrayList<>(accounts);
wrong.sort((x, y) -> x.balance() - y.balance());
IO.println("subtraction: " + wrong.getFirst().id());
accounts.sort(Comparator.comparingInt(Account::balance));
IO.println("comparingInt: " + accounts.getFirst().id());
}
// Output:
// subtraction: a
// comparingInt: b
Discussion