Terminal ops & Collectors
forEach, reduce, match, find, and the full Collectors toolbox including groupingBy.
Making the pipeline produce something
Terminal operations trigger the pipeline and produce a concrete result (or side-effect). Key terminals:
stream.forEach(System.out::println); // side-effect, returns void
long c = stream.count(); // number of elements
Optional<String> first = stream.findFirst(); // first element (if any)
booleans:
boolean any = stream.anyMatch(s -> s.startsWith("A"));
boolean all = stream.allMatch(s -> s.length() > 1);
boolean none = stream.noneMatch(s -> s.isEmpty());
Optional<Integer> min = IntStream.of(3,1,2).boxed().min(Comparator.naturalOrder());
reduce folds all elements into one value:
int sum = Stream.of(1, 2, 3, 4)
.reduce(0, (acc, n) -> acc + n); // identity=0, accumulator
collect(Collector) is the most versatile terminal — it feeds elements into a mutable container:
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
String joined = stream.collect(Collectors.joining(", ", "[", "]"));
Map<Integer, List<String>> byLen =
names.stream().collect(Collectors.groupingBy(String::length));
Map<Boolean, List<String>> byBlank =
names.stream().collect(Collectors.partitioningBy(String::isBlank));
groupingBy groups by a classifier function — the result is a Map<K, List<V>>. A downstream collector can further reduce each group:
Map<Integer, Long> countByLength =
names.stream()
.collect(Collectors.groupingBy(
String::length, // classifier
Collectors.counting() // downstream: count each group
));
// {3=["Bob"], 5=["Alice", "Anna" — wait, Anna is 4...]
// i.e. groups words by their character count, then counts each group
partitioningBy is a specialised groupingBy with a Predicate — result always has exactly two keys: true and false.