How to Sort a List of Integers Using Java Stream API

In this tutorial, I will demonstrate how to sort a list of integers using the Java Stream API. Sorting is a common operation when working with collections, and it is frequently discussed in Java coding interviews.

Java provides several ways to sort a list, including Collections.sort(), List.sort(), and the Stream API. The Stream API offers a concise way to process a collection and produce a sorted result without modifying the original list.

The sorted() method sorts stream elements in their natural order. For integers, natural order means ascending numerical order. You can also provide a Comparator to sort numbers in descending order or apply a custom sorting rule.

In this tutorial, you will learn how to sort integers in ascending and descending order, how to use custom comparators, and how Stream sorting differs from sorting a list directly.

1. Problem Statement

Write a Java program to sort a list of integers using the Stream API.

For example, consider the following list:

Input:
[50, 10, 40, 20, 30]

Ascending Order:
[10, 20, 30, 40, 50]

Descending Order:
[50, 40, 30, 20, 10]

The program should demonstrate both ascending and descending sorting using Java Streams.

2. Method 1: Sort Integers in Ascending Order Using Stream.sorted()

The simplest approach is to use the sorted() method.

For a stream of integers, this method sorts the elements in ascending order according to their natural ordering.

Java Program

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SortIntegersAscending {

    public static void main(String[] args) {

        List<Integer> numbers =
                Arrays.asList(50, 10, 40, 20, 30);

        System.out.println("Original List: " + numbers);

        // Sort integers in ascending order
        List<Integer> sortedNumbers = numbers.stream()
                .sorted()
                .collect(Collectors.toList());

        System.out.println("Ascending Order: "
                + sortedNumbers);
    }
}

Output

Original List: [50, 10, 40, 20, 30]
Ascending Order: [10, 20, 30, 40, 50]

Step-by-Step Explanation

Step 1: Create a List of Integers

List<Integer> numbers =
        Arrays.asList(50, 10, 40, 20, 30);

This statement creates a list containing five integers in an unsorted order.

Step 2: Convert the List into a Stream

numbers.stream()

The stream() method creates a sequential stream from the list.

Step 3: Sort the Elements

.sorted()

The sorted() method arranges the integers in ascending numerical order.

Because Integer implements Comparable, Java can determine the natural ordering of its values.

Step 4: Collect the Sorted Elements

.collect(Collectors.toList());

This terminal operation collects the sorted stream elements into a List.

The original list remains unchanged because sorting the stream does not modify the source collection.

3. Method 2: Sort Integers in Descending Order Using Stream.sorted()

To sort integers from largest to smallest, provide a Comparator to the sorted() method.

Java provides Comparator.reverseOrder(), which reverses the natural ordering of elements.

Java Program

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class SortIntegersDescending {

    public static void main(String[] args) {

        List<Integer> numbers =
                Arrays.asList(50, 10, 40, 20, 30);

        System.out.println("Original List: " + numbers);

        // Sort integers in descending order
        List<Integer> sortedNumbers = numbers.stream()
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());

        System.out.println("Descending Order: "
                + sortedNumbers);
    }
}

Output

Original List: [50, 10, 40, 20, 30]
Descending Order: [50, 40, 30, 20, 10]

How Comparator.reverseOrder() Works

The Comparator.reverseOrder() method returns a Comparator that applies the reverse of the elements' natural ordering.

For integer values:

  • Natural order arranges numbers from smallest to largest.
  • Reverse order arranges numbers from largest to smallest.

This approach is simple, readable, and commonly used in modern Java applications.

4. Method 3: Sort Integers Using Stream.toList() in Java 16+

Starting with Java 16, the Stream API provides the toList() method, which makes collecting stream results shorter.

You can use it instead of collect(Collectors.toList()) when an unmodifiable result list is acceptable.

Java Program Using Stream.toList()

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class SortIntegersUsingToList {

    public static void main(String[] args) {

        List<Integer> numbers =
                Arrays.asList(50, 10, 40, 20, 30);

        // Ascending order
        List<Integer> ascending = numbers.stream()
                .sorted()
                .toList();

        // Descending order
        List<Integer> descending = numbers.stream()
                .sorted(Comparator.reverseOrder())
                .toList();

        System.out.println("Original List: " + numbers);
        System.out.println("Ascending Order: " + ascending);
        System.out.println("Descending Order: " + descending);
    }
}

Output

Original List: [50, 10, 40, 20, 30]
Ascending Order: [10, 20, 30, 40, 50]
Descending Order: [50, 40, 30, 20, 10]

Important: Stream.toList() Returns an Unmodifiable List

The List returned by Stream.toList() is unmodifiable.

For example, the following code throws an UnsupportedOperationException:

List<Integer> sortedNumbers = numbers.stream()
        .sorted()
        .toList();

// Throws UnsupportedOperationException
sortedNumbers.add(60);

If you need a modifiable ArrayList, create one explicitly:

import java.util.ArrayList;

ArrayList<Integer> sortedNumbers = new ArrayList<>(
        numbers.stream().sorted().toList()
);

sortedNumbers.add(60);

This creates a mutable ArrayList containing the sorted elements.

5. Method 4: Sort Integers Using a Custom Comparator

You can also provide a custom Comparator using a lambda expression.

For example, the following program sorts integers in descending order using Integer.compare().

Java Program

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SortUsingCustomComparator {

    public static void main(String[] args) {

        List<Integer> numbers =
                Arrays.asList(50, 10, 40, 20, 30);

        List<Integer> descending = numbers.stream()
                .sorted((a, b) -> Integer.compare(b, a))
                .collect(Collectors.toList());

        System.out.println("Descending Order: "
                + descending);
    }
}

Output

Descending Order: [50, 40, 30, 20, 10]

The expression Integer.compare(b, a) compares the second value with the first, producing descending order.

Important: Prefer Integer.compare(b, a) over b - a. Subtracting integers in a Comparator can overflow for extreme values and produce an incorrect ordering.

6. Dry Run: Sorting Integers in Ascending Order

Consider the following list:

Original List:
[50, 10, 40, 20, 30]

When the program executes the stream pipeline, the sorted() operation arranges the elements according to their natural order.

Operation Purpose Result
Create List Store integer elements [50, 10, 40, 20, 30]
stream() Create a sequential stream Elements: 50, 10, 40, 20, 30
sorted() Apply natural ordering 10, 20, 30, 40, 50
collect() Create a result List [10, 20, 30, 40, 50]

The original list remains unchanged throughout the operation.

7. Sort a List Containing Duplicate and Negative Integers

The Stream API also handles duplicate values, negative numbers, and zero correctly.

Java Program

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class SortNegativeAndDuplicateNumbers {

    public static void main(String[] args) {

        List<Integer> numbers =
                Arrays.asList(10, -5, 30, 10, 0, -20, 30);

        List<Integer> ascending = numbers.stream()
                .sorted()
                .toList();

        List<Integer> descending = numbers.stream()
                .sorted(Comparator.reverseOrder())
                .toList();

        System.out.println("Original List: " + numbers);
        System.out.println("Ascending Order: " + ascending);
        System.out.println("Descending Order: " + descending);
    }
}

Output

Original List: [10, -5, 30, 10, 0, -20, 30]
Ascending Order: [-20, -5, 0, 10, 10, 30, 30]
Descending Order: [30, 30, 10, 10, 0, -5, -20]

Notice that sorting does not remove duplicate elements. The repeated values remain in the sorted result.

8. Time and Space Complexity

The sorted() operation sorts the stream elements using their natural ordering or the supplied Comparator.

Operation Time Complexity Additional Space
Ascending Stream sorting O(n log n) O(n)
Descending Stream sorting O(n log n) O(n)
Custom Comparator sorting O(n log n) O(n)

Here, n represents the number of elements in the list.

Sorting generally requires O(n log n) time for arbitrary input. The Stream API also uses additional memory to process sorted elements and create the resulting list.

The sorted() method is a stateful intermediate operation. It must consider the elements of the stream before producing them in sorted order.

9. Stream.sorted() vs. Collections.sort() vs. List.sort()

Java provides multiple sorting methods. The best choice depends on whether you want to modify the original list or create a sorted result.

Feature Stream.sorted() Collections.sort() List.sort()
Modifies original list No Yes Yes
Returns sorted elements Yes, through a stream No, sorts in place No, sorts in place
Supports Comparator Yes Yes Yes
Best use case Stream processing pipelines Traditional mutable list sorting Direct mutable list sorting

Use Stream.sorted() when you want to keep the original list unchanged or combine sorting with other stream operations.

If you simply need to sort a mutable list directly, List.sort() is often a more straightforward choice.

10. Important Edge Cases

Case 1: Empty List

Input:
[]

Output:
[]

The Stream API handles an empty list without errors.

Case 2: Single Element

Input:
[42]

Output:
[42]

A list containing one element is already sorted.

Case 3: Already Sorted List

Input:
[10, 20, 30, 40]

Ascending Output:
[10, 20, 30, 40]

The values remain in ascending order.

Case 4: Duplicate Values

Input:
[30, 10, 20, 10, 30]

Ascending Output:
[10, 10, 20, 30, 30]

Duplicate elements are preserved.

Case 5: List Contains Null Values

Natural ordering does not support comparing null Integer values. Calling sorted() on a list containing null elements can throw a NullPointerException.

If null values are expected, use a null-aware Comparator:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class SortIntegersWithNulls {

    public static void main(String[] args) {

        List<Integer> numbers =
                Arrays.asList(30, null, 10, 20, null);

        List<Integer> sorted = numbers.stream()
                .sorted(Comparator.nullsLast(
                        Comparator.naturalOrder()))
                .toList();

        System.out.println(sorted);
    }
}

Output

[10, 20, 30, null, null]

The Comparator.nullsLast() method places null values after all non-null integers.

11. Common Mistakes to Avoid

Mistake 1: Forgetting the Terminal Operation

The sorted() method is an intermediate operation. Stream pipelines are evaluated when a terminal operation runs.

To store the sorted values, collect the stream into a list:

List<Integer> sorted = numbers.stream()
        .sorted()
        .toList();

Mistake 2: Assuming the Original List Is Modified

The Stream API does not sort the original list in place.

If you want to replace the original list variable with the sorted result, you must assign the result explicitly. If you need to modify the existing list object, use List.sort().

Mistake 3: Using Subtraction in a Comparator

Avoid writing:

.sorted((a, b) -> b - a)

Integer subtraction may overflow for extreme values.

Instead, use:

.sorted((a, b) -> Integer.compare(b, a))

Or simply:

.sorted(Comparator.reverseOrder())

Mistake 4: Trying to Modify a List Returned by Stream.toList()

Remember that Stream.toList() returns an unmodifiable list.

Create a new ArrayList if you need to add, remove, or replace elements later.

12. Frequently Asked Questions

Q1. How do I sort a list of integers using Java Streams?

Use numbers.stream().sorted().toList() to sort integers in ascending order. This form requires Java 16 or later.

Q2. How do I sort integers in descending order using Java Streams?

Use numbers.stream().sorted(Comparator.reverseOrder()).toList().

Q3. Does Stream.sorted() modify the original list?

No. It produces a sorted stream of elements without changing the original list.

Q4. What is the difference between sorted() and sort() in Java?

The sorted() method operates on streams, while List.sort() sorts a mutable list directly.

Q5. What is the time complexity of Stream.sorted()?

The typical time complexity for sorting an arbitrary list of integers is O(n log n).

Q6. Can Stream.sorted() handle duplicate elements?

Yes. Duplicate values are preserved. Sorting does not remove duplicates.

Q7. Can I remove duplicates and sort integers in one stream pipeline?

Yes. Combine distinct() and sorted():

List<Integer> result = numbers.stream()
        .distinct()
        .sorted()
        .toList();

This removes duplicate integers and returns the remaining values in ascending order.

13. Conclusion

In this tutorial, you learned how to sort a list of integers using the Java Stream API.

  • Ascending order: Use stream().sorted().
  • Descending order: Use stream().sorted(Comparator.reverseOrder()).
  • Custom sorting: Pass a Comparator to the sorted() method.
  • Java 16 and later: Use toList() to collect an unmodifiable result.
  • Java 8 compatibility: Use collect(Collectors.toList()).

The Stream API is particularly useful when sorting is part of a larger data-processing operation. You can combine sorting with filtering, mapping, and removing duplicates to create readable collection-processing pipelines.

For Java coding interviews, I recommend understanding both Stream.sorted() and List.sort(), including the difference between creating a sorted result and modifying an existing list.

14. Official Java Documentation

For more information about the Java APIs used in this tutorial, refer to the official Oracle documentation:

Continue Learning with Java Guides

Want to take your programming skills to the next level? Explore my practical Udemy courses on Java, Spring Boot, Microservices, Full-Stack Development, Docker, and Generative AI. You can also follow my structured learning roadmaps to become a professional developer.

By Ramesh Fadatare - Udemy Instructor Partner

Comments