A [Stream](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html>) will only be traversed when there is a terminal operation, like [count()](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#count-->), [collect()](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.stream.Collector->) or [forEach()](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer->). Otherwise, no operation on the [Stream](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html>) will be performed.

In the following example, no terminal operation is added to the [Stream](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html>), so the [filter()](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#filter-java.util.function.Predicate->) operation will not be invoked and no output will be produced because [peek()](<https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer->) 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: