Skip to content

Other Operations

We categorize the operations provided by Stream into two types: transformation operations and aggregation operations. Besides the commonly introduced operations, Stream also offers a series of very useful methods.

Sorting

Sorting the elements of a Stream is very straightforward; simply call the sorted() method:

java
import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Orange", "apple", "Banana")
            .stream()
            .sorted()
            .collect(Collectors.toList());
        System.out.println(list);
    }
}

This method requires that each element of the Stream implements the Comparable interface. If you want to customize the sorting, you can pass a specific Comparator:

java
List<String> list = List.of("Orange", "apple", "Banana")
    .stream()
    .sorted(String::compareToIgnoreCase)
    .collect(Collectors.toList());

Note: sorted() is merely a transformation operation; it returns a new Stream.

Distinct

To remove duplicate elements from a Stream, there is no need to first convert it to a Set; you can directly use distinct():

java
List.of("A", "B", "A", "C", "B", "D")
    .stream()
    .distinct()
    .collect(Collectors.toList()); // [A, B, C, D]

Skipping and Limiting

Skipping and limiting operations are often used to convert an infinite Stream into a finite one. skip() is used to skip the first N elements of the current Stream, and limit() is used to take at most the first N elements of the current Stream:

java
List.of("A", "B", "C", "D", "E", "F")
    .stream()
    .skip(2) // Skip A, B
    .limit(3) // Take C, D, E
    .collect(Collectors.toList()); // [C, D, E]

Skipping and limiting operations are also transformation operations and will return a new Stream.

Merging

To merge two Streams into one, you can use the static method Stream.concat():

java
Stream<String> s1 = List.of("A", "B", "C").stream();
Stream<String> s2 = List.of("D", "E").stream();
// Merge:
Stream<String> s = Stream.concat(s1, s2);
System.out.println(s.collect(Collectors.toList())); // [A, B, C, D, E]

flatMap

If the elements of a Stream are collections:

java
Stream<List<Integer>> s = Stream.of(
        Arrays.asList(1, 2, 3),
        Arrays.asList(4, 5, 6),
        Arrays.asList(7, 8, 9));

and we want to convert the above Stream into a Stream<Integer>, we can use flatMap():

java
Stream<Integer> i = s.flatMap(list -> list.stream());

Thus, flatMap() refers to mapping each element of the Stream (here, a List) to a Stream, and then merging them into a new Stream:

┌─────────────┬─────────────┬─────────────┐
│┌───┬───┬───┐│┌───┬───┬───┐│┌───┬───┬───┐│
││ 1 │ 2 │ 3 │││ 4 │ 5 │ 6 │││ 7 │ 8 │ 9 ││
│└───┴───┴───┘│└───┴───┴───┘│└───┴───┴───┘│
└─────────────┴─────────────┴─────────────┘

                     │flatMap(List -> Stream)



   ┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │
   └───┴───┴───┴───┴───┴───┴───┴───┴───┘

Parallel Processing

By default, processing the elements of a Stream is single-threaded, meaning elements are processed one by one. However, often we want to process Stream elements in parallel, especially when dealing with a large number of elements, as parallel processing can significantly speed up the processing.

Converting a regular Stream into a parallel Stream is very simple; just use parallel():

java
Stream<String> s = ...
String[] result = s.parallel() // Convert to a parallel Stream
                   .sorted() // Can perform parallel sorting
                   .toArray(String[]::new);

Once converted using parallel(), the Stream will perform subsequent operations in parallel whenever possible. We do not need to write any multi-threaded code to enjoy the execution efficiency improvements brought by parallel processing.

Other Aggregation Methods

Besides reduce() and collect(), Stream provides some other commonly used aggregation methods:

  • count(): Returns the number of elements.
  • max(Comparator<? super T> cp): Finds the maximum element.
  • min(Comparator<? super T> cp): Finds the minimum element.

For IntStream, LongStream, and DoubleStream, additional aggregation methods are provided:

  • sum(): Sums all elements.
  • average(): Calculates the average of all elements.

There are also methods to test whether Stream elements satisfy certain conditions:

  • boolean allMatch(Predicate<? super T>): Tests whether all elements satisfy the condition.
  • boolean anyMatch(Predicate<? super T>): Tests whether at least one element satisfies the condition.

The last commonly used method is forEach(), which can iterate over each element of the Stream. We often pass System.out::println to print the elements of the Stream:

java
Stream<String> s = ...
s.forEach(str -> {
    System.out.println("Hello, " + str);
});

Summary

Common operations provided by Stream include:

  • Transformation Operations: map(), filter(), sorted(), distinct();
  • Merging Operations: concat(), flatMap();
  • Parallel Processing: parallel();
  • Aggregation Operations: reduce(), collect(), count(), max(), min(), sum(), average();
  • Other Operations: allMatch(), anyMatch(), forEach().
Other Operations has loaded