Skip to content

Commit 8093446

Browse files
committed
docs(stream): add documentation for downstream operations
* Explain the concept and usage of downstream collectors in Java Streams
1 parent aed4188 commit 8093446

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# What is a downstream operation in Java Streams?
2+
3+
In Java Streams, **downstream** usually means a **collector passed inside another collector**.
4+
5+
You commonly see this in:
6+
- `Collectors.groupingBy(...)`
7+
- `Collectors.partitioningBy(...)`
8+
- `Collectors.teeing(...)`
9+
10+
The outer collector decides the buckets (for example, by department or by pass/fail), and the downstream collector decides **what to do with each bucket**.
11+
12+
## Why is it used?
13+
14+
Downstream collectors help you:
15+
- aggregate grouped data (`counting`, `summingInt`, `averagingDouble`)
16+
- transform values while grouping (`mapping`)
17+
- post-process final grouped results (`collectingAndThen`)
18+
- build nested summaries (for example, group then average)
19+
20+
Without downstream collectors, you often need extra loops or multiple passes.
21+
22+
## Simple examples
23+
24+
### 1) Count people per department
25+
26+
```java
27+
var countByDept = employees.stream()
28+
.collect(Collectors.groupingBy(Employee::department, Collectors.counting()));
29+
```
30+
31+
Here:
32+
- outer collector: `groupingBy(Employee::department)`
33+
- downstream collector: `counting()`
34+
35+
### 2) Collect employee names per department
36+
37+
```java
38+
var namesByDept = employees.stream()
39+
.collect(Collectors.groupingBy(
40+
Employee::department,
41+
Collectors.mapping(Employee::name, Collectors.toList())
42+
));
43+
```
44+
45+
Here the downstream `mapping(...)` transforms each `Employee` into only a name before collecting.
46+
47+
### 3) Average salary by department
48+
49+
```java
50+
var avgSalaryByDept = employees.stream()
51+
.collect(Collectors.groupingBy(
52+
Employee::department,
53+
Collectors.averagingDouble(Employee::salary)
54+
));
55+
```
56+
57+
## Common downstream collectors
58+
59+
- `counting()`
60+
- `summingInt / summingLong / summingDouble`
61+
- `averagingInt / averagingLong / averagingDouble`
62+
- `maxBy / minBy`
63+
- `mapping(...)`
64+
- `filtering(...)` (Java 9+)
65+
- `flatMapping(...)` (Java 9+)
66+
- `collectingAndThen(...)`
67+
- nested `groupingBy(...)`
68+
69+
## Difference from stream intermediate operations
70+
71+
- Intermediate operations like `map`, `filter`, `sorted` run on the stream pipeline.
72+
- Downstream collectors run **inside `collect(...)`**, typically per group/partition.
73+
74+
## Tips
75+
76+
- Prefer clear, readable collector chains over deeply nested one-liners.
77+
- Use `toList()` where appropriate in project code; use `Collectors.toList()` when required by APIs like `mapping(...)`.
78+
- For `maxBy/minBy`, remember values are `Optional`.
79+

0 commit comments

Comments
 (0)