Working with Stream API
1. Creating Streams
Source Factory Notes
Collection coll.stream() / parallelStream()Most common
Array Arrays.stream(arr)Primitive overloads
Values Stream.of(a, b, c)Varargs
Range IntStream.range(0, n)Half-open
Generate Stream.generate(supplier)Infinite
Iterate Stream.iterate(seed, fn) / iterate(seed, hasNext, next)Bounded form Java 9+
Files Files.lines(path)Auto-closed
Builder Stream.builder().add(x).build()Imperative add
Empty Stream.empty()Avoid null
Operation Stateless? Purpose
filter(Predicate)Yes Keep matching
map(Function)Yes 1:1 transform
flatMap(Function)Yes 1:N flatten
mapMulti Java 16+ Yes Imperative flatten
distinct()Stateful Dedup via equals
sorted()Stateful Buffers all elements
peek(Consumer)Yes Debug only
limit(n) / skip(n)Stateful Slicing
takeWhile / dropWhileStateful Java 9+
3. Using Terminal Operations
Operation Returns Short-circuits
forEach / forEachOrderedvoid No
collect(Collector)R No
toList() Java 16+ Unmodifiable List No
reduceT or Optional<T> No
countlong No (sized → yes)
min / maxOptional No
findFirst / findAnyOptional Yes
anyMatch / allMatch / noneMatchboolean Yes
toArrayObject[] / T[] No
4. Understanding Stream Pipeline
Source → [Intermediate ops fused] → Terminal op
(lazy, no work) (triggers traversal)
Per element: filter → map → flatMap → ... → terminal
(pull model — terminal pulls one element through the pipeline)
Property Meaning
Lazy Intermediate ops do nothing until terminal
Single use Stream cannot be reused after terminal
Fusion Adjacent stateless ops combined
Encounter order Preserved unless unordered()
5. Working with Primitive Streams
Stream Box Specialized Ops
IntStreamInteger sum, average, summaryStatistics, range
LongStreamLong Same as IntStream
DoubleStreamDouble Same minus range
Example: Avoid boxing
int total = orders. stream (). mapToInt (Order :: amount). sum ();
IntSummaryStatistics st = orders. stream (). mapToInt (Order :: amount). summaryStatistics ();
6. Using Collectors
Collector Result
toList() / toSet()List / Set
toUnmodifiableList()Immutable list
toMap(k, v[, merge])Map
groupingBy(classifier[, downstream])Map<K, List<V>>
partitioningBy(predicate)Map<Boolean, List>
counting()Long count
summingInt / averagingIntAggregation
joining(delim, prefix, suffix)String
mapping / filtering / flatMappingDownstream adapters
teeing(c1, c2, merger) Java 12+ Combine two collectors
Example: Group + summarize
Map< String , Long > byDept = emps. stream ()
. collect (Collectors. groupingBy (Employee :: dept, Collectors. counting ()));
Map< String , Double > avgSalary = emps. stream ()
. collect (Collectors. groupingBy (Employee :: dept, Collectors. averagingDouble (Employee :: salary)));
7. Creating Custom Collectors (Collector.of())
Argument Type Role
supplier Supplier<A>New mutable container
accumulator BiConsumer<A,T>Add element
combiner BinaryOperator<A>Merge for parallel
finisher Function<A,R>Optional final transform
characteristics CONCURRENT, UNORDERED, IDENTITY_FINISHHints
Example: Top-N collector
public static < T > Collector < T, ? , List < T >> topN ( int n, Comparator <? super T > cmp) {
return Collector. of (
() -> new PriorityQueue< T >(cmp),
(q, x) -> { q. add (x); if (q. size () > n) q. poll (); },
(a, b) -> { b. forEach (a :: add); while (a. size () > n) a. poll (); return a; },
q -> q. stream (). sorted (cmp. reversed ()). toList ()
);
}
8. Using Parallel Streams (parallelStream())
Aspect Detail
Pool Common ForkJoinPool (size = cores − 1)
Custom pool Submit stream task to ForkJoinPool
Good fit Large N, CPU-bound, splittable source
Bad fit Small N, blocking I/O, ordered ops
Reduce Identity must satisfy combiner(identity, x) == x
Warning: Avoid shared mutable state and side effects in parallel streams; collisions cause non-determinism.
Factor Impact
Auto-boxing Use primitive streams
Stateful ops Buffer entire stream (sorted, distinct)
Splitability ArrayList good; LinkedList poor
Short-circuit Place selective filters early
Object overhead vs plain for-loop ~ 2-5×
10. Handling Infinite Streams
Pattern Example
Bounded by limit Stream.iterate(0, i->i+1).limit(100)
Bounded form Stream.iterate(0, i->i<100, i->i+1)
takeWhile stream.takeWhile(x -> x < 50)
findFirst on infinite OK if predicate eventually matches
Warning: Never call count(), collect(), or sorted() on an unbounded stream.
11. Using FlatMap for Nested Structures
Variant Maps Each Element To
flatMapStream<R>
flatMapToInt/Long/DoublePrimitive stream
mapMultiPushes 0..N to consumer
Example: Flatten orders
List< Item > allItems = orders. stream ()
. flatMap (o -> o. items (). stream ())
. toList ();
12. Debugging Streams (peek())
Tool Use
peekInspect elements between ops
Logger.atDebug Conditional logging
IntelliJ Stream Trace Visualize per-stage values
Stream Debugger plugin Step through pipeline
Note: JIT may elide peek if its result is unused after a non-stateful terminal — don't rely on it for production side effects.
13. Using takeWhile() and dropWhile() (Java 9+)
Method Behavior Ordered Stream
takeWhile(p)Take prefix while p holds Stops at first false
dropWhile(p)Skip prefix while p holds Emits rest
vs filter Position-aware vs all elements Different semantics
14. Using Stream.toList() (Java 16+)
Aspect toList()collect(toList())
Mutability Unmodifiable Mutable ArrayList
Nulls Allowed Allowed
Performance Slightly faster (presized) Baseline
Recommendation Default choice Use only when mutability needed