3 · Reliability and project · 25 MIN
Streams and grouping without mutation
A stream describes a pipeline over data.
filter discards values that do not meet the predicate, mapToInt changes the stream to primitive integers, and sum is a terminal operation. Nothing runs until a terminal operation requests the result. This sample uses a pure pipeline without shared mutable state. Do not assume parallelStream is faster: scheduling, ordering and the cost of each element matter. Streams can be consumed only once; create a fresh stream for a separate result. For large monetary totals use a range-appropriate representation and checked arithmetic.
Prefer collectors or reductions with defined identities.
Read the example
import java.util.*;
public class Main {
record Expense(String category, int amount) {}
public static void main(String[] args) {
var rows = List.of(new Expense("food",5),new Expense("travel",3),new Expense("food",7));
int total = rows.stream().filter(e -> e.category().equals("food")).mapToInt(Expense::amount).sum();
System.out.println(total);
System.out.println(rows.size());
}
}Check the expected output
12 3
Your challenge
Return a Map of category totals using groupingBy and summingInt, then print categories in sorted order.
Solution cost: O(n) traversal; grouping requires O(k) category storage. time · Account for collection storage separately from the returned result. space
Common trap
Mutating a shared accumulator inside a parallel stream introduces races.
Study the project implementation
import java.util.*;
public class Main {
record Expense(String category, int amount) {}
public static void main(String[] args) {
var rows = List.of(new Expense("food",5),new Expense("travel",3),new Expense("food",7));
int total = rows.stream().filter(e -> e.category().equals("food")).mapToInt(Expense::amount).sum();
System.out.println(total);
System.out.println(rows.size());
}
}Further reading: Official documentation
Next lesson: Project · Inventory with checked updates →