Community Question Bundle
Java Streams and Collectors Deep Quiz
A 4-question reference set on Java streams beyond the basics: laziness and short-circuiting, downstream collectors in groupingBy, toMap collision handling, and when parallel streams actually pay off.
Java Streams and Collectors Deep Quiz
A 4-question reference set on Java streams beyond the basics: laziness and short-circuiting, downstream collectors in groupingBy, toMap collision handling, and when parallel streams actually pay off.
By CodeSnatch
December 6, 2025
·
Updated May 18, 2026
236 views
4
Rate
Java streams are lazy: intermediate operations defer until a terminal operation fires. What does this enable, and where does it bite when paired with side effects?
Examples
Example 1:
Input: list.stream().filter(x -> x > 100).map(this::loadOrder).findFirst()
Output: Stream short-circuits after the first match; loadOrder is called only once
Explanation: Laziness means filter+map fuse into one pass and stop at the terminal's first satisfied element.Example 2:
Input: list.stream().peek(x -> log.info("seen {}", x)).filter(x -> x > 100).collect(toList())
Output: peek runs for every element, but ONLY because there's a downstream filter+collect; without a terminal op, peek runs zero times
Explanation: Side-effect-inside-stream is a smell; if the terminal op short-circuits or is removed, peek behavior changes.Collectors.groupingBy returns Map<K, List<V>> by default but accepts a downstream collector. Walk through three common downstream collectors and the shape they produce.
Examples
Example 1:
Input: orders.stream().collect(Collectors.groupingBy(Order::status))
Output: Map<Status, List<Order>> // default downstream is toList()
Explanation: One-argument groupingBy buckets elements; the downstream value is a list.Example 2:
Input: orders.stream().collect(Collectors.groupingBy(Order::status, Collectors.counting()))
Output: Map<Status, Long>
Explanation: Downstream Collectors.counting() reduces each group to its size.Collectors.toMap throws on duplicate keys by default. What is the merge function for, and what is the standard fix when keys can collide?
Examples
Example 1:
Input: orders.stream().collect(Collectors.toMap(Order::userId, Order::amount))
Output: Throws IllegalStateException if two orders have the same userId
Explanation: Two-arg toMap assumes keys are unique; collision throws to surface the bug.Example 2:
Input: orders.stream().collect(Collectors.toMap(Order::userId, Order::amount, Double::sum))
Output: Map<UserId, Double> with summed amounts per user
Explanation: The third argument is a BinaryOperator that resolves collisions.Parallel streams use the common ForkJoinPool. When does .parallel() actually help, and what is the right way to scale it for I/O?
Examples
Example 1:
Input: 10M integers in a list; .parallelStream().mapToInt(...).sum()
Output: ~Nx speedup where N is core count; CPU-bound numeric work parallelizes well
Explanation: Stream parallelism shines on CPU-bound, side-effect-free reductions over large in-memory collections.Example 2:
Input: 100 URLs; .parallelStream().map(url -> httpGet(url)).collect(toList())
Output: Limited speedup; ForkJoinPool.commonPool() has only Runtime.getRuntime().availableProcessors()-1 threads
Explanation: Common pool is sized for CPU; I/O parallelism needs a custom pool or a different abstraction.