map, filter, and reduce: The Core Stream Operations
map, filter, and reduce: The Core Stream Operations
Three operations form the backbone of most stream pipelines: map transforms values, filter selects values, and reduce aggregates values. Together they replace most of the loops you used to write, and they read like a description of what you want rather than how to get it.
Map
map applies a function to every element and returns a stream of the results, one-for-one. Use it to extract fields, convert types, or calculate derived values. The input and output types do not need to match, which is what makes it so flexible when you reshape data.
Filter
filter keeps only elements that satisfy a predicate. It never changes individual elements; it only decides which ones continue down the pipeline. Combine it with map for the classic select-transform pattern, and keep predicates as small named methods for readability.
List<Order> orders = fetchOrders();
double total = orders.stream()
.filter(o -> o.isPaid())
.map(Order::getAmount)
.reduce(0.0, Double::sum);
System.out.printf("Total: %.2f", total);
Reduce
reduce folds the stream into a single value using an associative accumulation operation. The two-argument form takes an identity and a binary operator, as shown above. Avoid reduce for simple counts or sums when specialized streams exist; IntStream and LongStream ship with sum, average, max, and min primitives.
Chaining with method references
Method references like Order::getAmount and Double::sum make pipelines compact without sacrificing readability. A well-formed pipeline reads like a sentence: take paid orders, get their amounts, add them up.
Key Points
maptransforms elements one-for-one.filterkeeps elements matching a predicate.reduceaggregates a stream into a single value.- Compose the three into the classic select-transform-aggregate pipeline.
- Prefer primitive streams when working with numeric ranges and sums.