43. Streams — Basics
Stream<T> is a lazy sequence of elements supporting pipeline-style operations — filter, map, flatMap, collect, reduce — that are evaluated only when a terminal operation is invoked.
Explanation
A stream pipeline has three parts: a source (collection, array, generator, I/O channel), zero or more intermediate operations (lazy — they return a new stream and don't execute until a terminal is called), and a terminal operation (eager — triggers evaluation and produces a result or side effect).
Key intermediate operations: filter(pred) keeps matching elements; map(fn) transforms each element; flatMap(fn) maps to a stream and flattens; sorted() / sorted(comparator) sorts; distinct() removes duplicates; peek(consumer) for debugging (don't use for side effects in production); limit(n) / skip(n) for windowing.
Key terminal operations: collect(collector) gathers into a collection; reduce(identity, accumulator) folds; forEach(consumer) side effects; count(), min(), max(), findFirst(), anyMatch(pred), allMatch(pred), noneMatch(pred).
Streams are not reusable — a stream can be consumed only once. Attempting to use it again throws IllegalStateException.
Example
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamsBasics {
record Product(String name, String category, double price) {}
public static void main(String[] args) {
var products = List.of(
new Product("Laptop", "Electronics", 999.99),
new Product("Phone", "Electronics", 699.00),
new Product("Desk", "Furniture", 299.50),
new Product("Chair", "Furniture", 199.00),
new Product("Monitor", "Electronics", 349.99)
);
// filter + map + collect
List<String> expensiveElectronics = products.stream()
.filter(p -> "Electronics".equals(p.category()))
.filter(p -> p.price() > 500)
.map(Product::name)
.sorted()
.collect(Collectors.toList());
System.out.println("Expensive electronics: " + expensiveElectronics);
// reduce: total price
double total = products.stream()
.mapToDouble(Product::price)
.sum(); // specialised: avoids boxing
System.out.printf("Total: $%.2f%n", total);
// flatMap: flatten lists
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4), List.of(5));
List<Integer> flat = nested.stream().flatMap(List::stream).collect(Collectors.toList());
System.out.println("Flat: " + flat);
// Terminal: count, min, max, findFirst
long count = products.stream().filter(p -> p.price() < 400).count();
System.out.println("Cheap products: " + count);
products.stream()
.max(java.util.Comparator.comparingDouble(Product::price))
.ifPresent(p -> System.out.println("Most expensive: " + p.name()));
// groupingBy
Map<String, List<Product>> byCategory = products.stream()
.collect(Collectors.groupingBy(Product::category));
byCategory.forEach((cat, ps) ->
System.out.println(cat + ": " + ps.stream().map(Product::name).toList()));
}
}