Java Language Consuming Streams


Example

A Stream will only be traversed when there is a terminal operation, like count(), collect() or forEach(). Otherwise, no operation on the Stream will be performed.

In the following example, no terminal operation is added to the Stream, so the filter() operation will not be invoked and no output will be produced because peek() is NOT a terminal operation.

IntStream.range(1, 10).filter(a -> a % 2 == 0).peek(System.out::println);

Live on Ideone

This is a Stream sequence with a valid terminal operation, thus an output is produced.

You could also use forEach instead of peek:

IntStream.range(1, 10).filter(a -> a % 2 == 0).forEach(System.out::println); 

Live on Ideone

Output:

2
4
6
8

After the terminal operation is performed, the Stream is consumed and cannot be reused.


Although a given stream object cannot be reused, it's easy to create a reusable Iterable that delegates to a stream pipeline. This can be useful for returning a modified view of a live data set without having to collect results into a temporary structure.

List<String> list = Arrays.asList("FOO", "BAR");
Iterable<String> iterable = () -> list.stream().map(String::toLowerCase).iterator();

for (String str : iterable) {
    System.out.println(str);
}
for (String str : iterable) {
    System.out.println(str);
}

Output:

foo
bar
foo
bar

This works because Iterable declares a single abstract method Iterator<T> iterator(). That makes it effectively a functional interface, implemented by a lambda that creates a new stream on each call.


In general, a Stream operates as shown in the following image:

Stream Operation

NOTE: Argument checks are always performed, even without a terminal operation:

try {
    IntStream.range(1, 10).filter(null);
} catch (NullPointerException e) {
    System.out.println("We got a NullPointerException as null was passed as an argument to filter()");
}

Live on Ideone

Output:

We got a NullPointerException as null was passed as an argument to filter()