Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

In Java 8, a stream pipeline processes data from a source through optional intermediate operations and a terminal operation. Use filter to select elements, map to transform them, and reduce to combine values into a result. The intermediate steps describe the work; the terminal operation starts it.

What a Java 8 stream is—and how its pipeline works

A stream is a sequence of elements that supports sequential or parallel aggregate operations. It is not a collection or a container that stores the results of each step. Instead, it describes a computation over a source, such as a collection.

A pipeline has three parts: a source, zero or more intermediate operations, and a terminal operation. Intermediate operations such as filter and map are lazy: they set up processing but do not, by themselves, traverse the source. Processing begins when a terminal operation is initiated, and elements are consumed as needed.

What filter, map, and reduce do

Operation Pipeline role What it produces Empty-stream behavior
filter(predicate) Intermediate A stream containing only elements for which the predicate is true. An empty stream remains empty.
map(function) Intermediate A stream of values produced by applying the function to each input element. An empty stream remains empty.
reduce(accumulator) without an identity Terminal An Optional containing the combined result, if one exists. Returns an empty Optional.
reduce(identity, accumulator) Terminal A result formed by combining the identity with the stream elements. Returns the identity.

filter: select elements

filter takes a predicate—a function that answers true or false for an element—and retains only elements for which it is true. For example, .filter(n -> n > 0) keeps positive numbers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

map: transform elements

map applies a function to each element and emits the resulting values. It does not select or discard values based on a condition. For example, .map(n -> n * 2) doubles each number that reaches that stage.

reduce: combine elements

reduce combines elements using an associative accumulation function. Associative means that grouping the same values in different ways does not change the result, as with addition: (a + b) + c equals a + (b + c). This property matters for reductions, including when a pipeline may run in parallel.

The overload without an identity returns an Optional because an empty stream has no element to use as a result. The overload with an identity returns a value even when the stream is empty, so its identity must genuinely match the accumulation operation. For addition, zero is the identity; for multiplication, it is one.

Putting the three operations together

This example keeps positive numbers, doubles them, then adds the results:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
int total = numbers.stream()
    .filter(n -> n > 0)
    .map(n -> n * 2)
    .reduce(0, Integer::sum);

If numbers contains 3, -2, and 4, the filter keeps 3 and 4, the map produces 6 and 8, and the reduction adds them to produce 14. If no positive numbers remain, the reduction returns its identity, 0.

The same selection-then-aggregation pattern can use a primitive stream when the mapped values are numeric:

int totalWeight = widgets.stream()
    .filter(widget -> widget.getColor() == RED)
    .mapToInt(Widget::getWeight)
    .sum();

mapToInt produces an IntStream, whose numeric operations include sum. Java 8 also provides LongStream and DoubleStream alongside reference streams such as Stream<T>.

Why a pipeline may appear to do nothing

Building intermediate stages does not execute them. A terminal operation is what initiates the work. In the examples above, reduce and sum are terminal operations. Other terminal operations include count and collect.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If you need a collection rather than a single aggregate value, use a collection-producing terminal operation such as collect; a stream itself is not a list. The choice of terminal operation determines the shape of the result: a count is a number, a reduction is an aggregate value, and a collection-producing operation gives you a collection.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Sequential and parallel streams

Java 8 supports both execution modes. For a collection, stream() creates a sequential stream, while parallelStream() creates a parallel stream. Parallel execution is an option, not a guarantee of faster completion: the API defines the modes but does not promise a speedup for every task. A reduction intended for parallel execution must also use an appropriate associative operation and identity.

Further reading

For a longer treatment of Java 8 lambdas and streams, Manning lists Java 8 in Action: Lambdas, streams, and functional-style programming, by Raoul-Gabriel Urma, Mario Fusco, and Alan Mycroft. Its August 2014 edition is 424 pages and is aimed at programmers familiar with Java and basic object-oriented programming. Manning also lists the newer Modern Java in Action; the older book is specifically about Java 8.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.