Java Stream API
Java Stream API tests building lazy pipelines, choosing intermediate versus terminal operations, and collecting results correctly.
Core rules
- A stream has a source, zero or more intermediate operations (
filter,map,flatMap,distinct,sorted,peek,limit,skip), and one terminal operation (forEach,collect,reduce,count,anyMatch,findFirst,min,max). - Intermediate operations are lazy and return a new stream; nothing runs until the terminal operation executes.
- A stream cannot be reused; doing so throws
IllegalStateException. maptransforms one element to one element;flatMaptransforms each element to a stream and flattens the results.collect(Collectors.groupingBy(classifier))returnsMap<K, List<T>>; a downstream collector such ascounting()ormapping()changes the value type.partitioningByalways returns a two-entryMap<Boolean, List<T>>.- Primitive streams (
IntStream,LongStream,DoubleStream) providesum(),average(),summaryStatistics(), andboxed().
Worked example
List<String> words = Arrays.asList("a","bb","cc");
Map<Integer,Long> byLen = words.stream().collect(Collectors.groupingBy(String::length, Collectors.counting()));
Result: {1=1, 2=2}. The classifier key is the length; counting() counts elements per group. Without the downstream collector the values would be List<String>.
Traps
- Forgetting the terminal operation means no processing occurs.
- Reusing a stream after a terminal operation is an error.
findFirst()returnsOptional; callingget()on empty throwsNoSuchElementException.Collectors.toMapthrowsIllegalStateExceptionon duplicate keys unless a merge function is supplied.sorted()on non-Comparableelements fails unless aComparatoris given.peekis intended for debugging; relying on it for side effects in a short-circuiting pipeline is unsafe.