Java Streams: filter, map and collect explained
November 4, 2026 · 2 min read
A Java stream is a pipeline: filter keeps the elements you want, map converts each one, and collect gathers the result, for example into groups with Collectors.groupingBy.
Example
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Sale> week = List.of(
new Sale("Bloom Café", "HE-5LB", 6), new Sale("Steam & Co", "HE-5LB", 8),
new Sale("Hearth", "SO-5LB", 3), new Sale("Juniper", "HE-5LB", 4),
new Sale("Bloom Café", "DC-5LB", 2), new Sale("Ferry Street", "SO-5LB", 5),
new Sale("Steam & Co", "SO-5LB", 6), new Sale("Hearth", "HE-5LB", 5));
List<Integer> big = week.stream()
.filter(s -> s.bags() >= 5)
.map(Sale::bags)
.toList();
System.out.println(big);
Map<String, Integer> bagsBySku = week.stream()
.collect(Collectors.groupingBy(Sale::sku, TreeMap::new,
Collectors.summingInt(Sale::bags)));
System.out.println(bagsBySku);
}
}
record Sale(String cafe, String sku, int bags) {}
Save it as Main.java and run java Main.java. Output:
[6, 8, 5, 6, 5]
{DC-5LB=2, HE-5LB=23, SO-5LB=14}
How it works
week.stream()is the source. The list itself never changes.filter(s -> s.bags() >= 5)keeps only sales of five bags or more.map(Sale::bags)turns each remainingSaleinto its bag count.toList()is the terminal operation. Streams are lazy, so nothing runs until you call one.
Collectors.groupingBy(Sale::sku, ...) builds a Map with one key per SKU. The last argument, summingInt(Sale::bags), is a downstream collector: it adds up the bags inside each group. Passing TreeMap::new just sorts the keys; without it you get a HashMap with no guaranteed order.
If you leave out the downstream collector, each key maps to a List<Sale> of its sales instead.
Common mistake
Forgetting the terminal operation. This compiles but does nothing:
week.stream().filter(s -> s.bags() >= 5).map(Sale::bags);
End the pipeline with toList(), collect(...), sum() or similar.
Another trap: toList() returns an unmodifiable list, so calling add on it throws UnsupportedOperationException. Copy it with new ArrayList<>(list) if you need to change it.
Takeaway
Think of a stream as filter, then map, then one terminal step. Add groupingBy with a downstream collector whenever you need totals per category.