Working with Modern Stream Features
1. Using Stream.toList() Method JAVA 16+
| Aspect | Detail |
|---|---|
| Returns | Unmodifiable List<T> |
| Allows null | Yes (unlike List.of) |
| Replaces | collect(Collectors.toList()) |
2. Using Stream.mapMulti() Method JAVA 16+
Example: mapMulti
Stream.of(1, 2, 3, 4, 5)
.<Integer>mapMulti((n, sink) -> {
if (n % 2 == 0) {
sink.accept(n);
sink.accept(n * n);
}
})
.forEach(System.out::println); // 2, 4, 4, 16
| Aspect | Detail |
|---|---|
| Signature | mapMulti(BiConsumer<T, Consumer<R>>) |
| Use case | 0..N elements per input without intermediate stream allocation |
3. Using Stream.teeing() Collector JAVA 12+
Example: teeing
record Stats(double avg, long count) {}
Stats s = numbers.stream().collect(
Collectors.teeing(
Collectors.averagingInt(Integer::intValue),
Collectors.counting(),
(avg, count) -> new Stats(avg, count)
));
| Aspect | Detail |
|---|---|
| Use | Combine TWO downstream collectors |
| Merger | BiFunction combines results |
4. Using Gatherers API JAVA 22+ PREVIEW
| Aspect | Detail |
|---|---|
| Purpose | Custom intermediate operations (parallel-friendly) |
| Method | stream.gather(gatherer) |
| Built-ins | windowFixed, windowSliding, fold, scan, mapConcurrent |
5. Creating Custom Gatherers
Example: Custom gatherer
Gatherer<Integer, ?, Integer> runningMax =
Gatherer.ofSequential(
() -> new int[]{ Integer.MIN_VALUE },
(state, n, downstream) -> {
state[0] = Math.max(state[0], n);
return downstream.push(state[0]);
}
);
| Component | Role |
|---|---|
| Initializer | Per-thread state |
| Integrator | Per-element processing |
| Combiner | Merge state (parallel) |
| Finisher | Emit final values |
6. Using windowFixed() Gatherer
Example: Fixed window
Stream.of(1,2,3,4,5,6,7)
.gather(Gatherers.windowFixed(3))
.forEach(System.out::println);
// [1,2,3], [4,5,6], [7]
| Aspect | Detail |
|---|---|
| Output | Non-overlapping lists of size n |
| Last window | May be smaller |
7. Using windowSliding() Gatherer
| Aspect | Detail |
|---|---|
| Output | Overlapping lists, sliding by 1 |
| Use case | Moving averages, n-grams |
8. Using fold() Gatherer
| Aspect | Detail |
|---|---|
| Behavior | Accumulate to single value with seed |
| Returns | Stream of one element |
Difference from reduce | Lazy intermediate, composes with stream pipeline |
9. Using scan() Gatherer
| Aspect | Detail |
|---|---|
| Behavior | Like fold but emits each intermediate accumulator |
| Use case | Running sums, prefix products |
10. Composing Gatherers
| Method | Use |
|---|---|
g1.andThen(g2) | Pipeline composition |
| Reusable | Build complex transforms from primitives |