sorted()

Sort stream elements by natural order or by a Comparator, with tie-breakers, reversed order and nulls handled, and know what sorting costs.

Signature
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)
  • Added in Java Java 8

Parameters

ParameterTypeDescription
$comparatoroptionalComparator<? super T>The order to sort by (second overload). Without it, elements are sorted by their natural order and must implement Comparable.

Return value

Stream<T>A new stream with the same elements in sorted order. For ordered streams the sort is stable, so equal elements keep their original order.

Errors

ClassCastException, when the terminal operation runs, if sorted() without a comparator meets elements that are not Comparable. Exceptions thrown by the comparator also surface at that point.

sorted() returns a stream with the elements in order. Without an argument it uses natural order, so elements must implement Comparable (strings, numbers, dates, enums). With a Comparator you decide the order, which is what you need for records and entities.

Building comparators

The Comparator factory methods cover nearly every case:

.sorted(Comparator.comparing(User::lastName))
.sorted(Comparator.comparingInt(Product::pricePence).thenComparing(Product::name))
.sorted(Comparator.comparing(Order::createdAt).reversed())
.sorted(Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))

thenComparing adds a tie-breaker. Always give one when the result feeds pagination: if two rows have the same price and the order between them is not fixed, page 2 can repeat or skip an item. A unique field such as the ID makes the order total.

Natural order surprises

Strings sort by UTF-16 code unit, so every uppercase letter comes before every lowercase one: "Zed" sorts before "apple". Use String.CASE_INSENSITIVE_ORDER for simple case-insensitive sorting, or a java.text.Collator for language-aware ordering of names.

Calling sorted() on a record or class that does not implement Comparable compiles, then throws ClassCastException when the terminal operation compares two elements. With zero or one element nothing is compared, so a test with a single item will not catch it.

Stability

For ordered streams (lists, arrays, Stream.of) the sort is stable: elements that compare equal stay in their original order. So sorting by a secondary key and then by a primary key in a separate step also works, though thenComparing is clearer.

Cost

sorted is a stateful operation. It has to see every element before it can emit the first, so it buffers the whole stream in memory and it never works on an infinite stream. sorted().limit(10) still sorts everything. For large data sets, let the database do ORDER BY ... LIMIT, which can use an index, and keep stream sorting for data already in memory. For the single smallest or largest element, min and max do it in one pass without a sort.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Order search results by relevance and then by ID so pagination is stable across requests.
  • Show the five slowest endpoints from in-memory latency metrics on an admin page.
  • Sort a small configuration list of plans by price for a pricing endpoint.

Frequently asked questions

How do I sort a stream in reverse order?

For natural order use sorted(Comparator.reverseOrder()). For a key, build the comparator and reverse it: sorted(Comparator.comparing(Order::createdAt).reversed()).

How do I sort a stream by more than one field?

Chain comparators: sorted(Comparator.comparing(User::lastName).thenComparing(User::firstName).thenComparingLong(User::id)). Each thenComparing only decides when the earlier keys are equal.

Why does Stream.sorted throw ClassCastException?

sorted() without arguments uses natural order and casts each element to Comparable. If your class or record does not implement Comparable, the cast fails the first time two elements are compared. Pass a Comparator instead.

More examples

Case-insensitive order with nulls at the end

Without nullsLast the comparator would throw NullPointerException on the null element. Wrapping it moves nulls to the end and sorts the rest case-insensitively.

Example · java
void main() {
    List<String> names = Arrays.asList("bob", null, "Alice", "carol");

    List<String> sorted = names.stream()
        .sorted(Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))
        .toList();
    IO.println(sorted);
}
// Output:
// [Alice, bob, carol, null]

sorted() needs Comparable elements

The record does not implement Comparable, so natural-order sorting fails as soon as two elements are compared. Tests with one element hide the bug; passing a Comparator fixes it.

Example · java
record Ticket(int id) {}

void main() {
    try {
        Stream.of(new Ticket(2), new Ticket(1)).sorted().toList();
    } catch (ClassCastException e) {
        IO.println("sorted() failed: " + e.getClass().getSimpleName());
    }

    // A single element is never compared, so this passes
    IO.println(Stream.of(new Ticket(2)).sorted().toList());

    IO.println(Stream.of(new Ticket(2), new Ticket(1))
        .sorted(Comparator.comparingInt(Ticket::id))
        .toList());
}
// Output:
// sorted() failed: ClassCastException
// [Ticket[id=2]]
// [Ticket[id=1], Ticket[id=2]]

Top N with sorted and limit

sorted then limit gives the top N from data already in memory, though it still sorts every element. For just the single largest, max does one pass without sorting.

Example · java
record Hit(String path, int count) {}

void main() {
    List<Hit> hits = List.of(
        new Hit("/login", 40),
        new Hit("/api/orders", 95),
        new Hit("/health", 12),
        new Hit("/api/users", 77));

    List<String> top2 = hits.stream()
        .sorted(Comparator.comparingInt(Hit::count).reversed())
        .limit(2)
        .map(h -> h.path() + "=" + h.count())
        .toList();
    IO.println(top2);

    Hit busiest = hits.stream().max(Comparator.comparingInt(Hit::count)).orElseThrow();
    IO.println(busiest.path());
}
// Output:
// [/api/orders=95, /api/users=77]
// /api/orders

Discussion

  • Be the first to comment on this lesson.