
Since Java 8, Streams let you describe what to do with a collection instead of writing loops. The code reads like a pipeline.
The data
record Sale(String region, String product, int qty, double price) {
double amount() { return qty * price; }
}
List<Sale> sales = List.of(
new Sale("North", "Keyboard", 12, 1299),
new Sale("South", "Mouse", 30, 599),
new Sale("North", "Monitor", 4, 11499),
new Sale("East", "Webcam", 6, 2799));
record (Java 16+) is a compact way to define a data class.
filter + map + collect
List<String> northProducts = sales.stream()
.filter(s -> s.region().equals("North")) // keep North rows
.map(Sale::product) // take the product name
.toList(); // [Keyboard, Monitor]
Totals and sorting
double total = sales.stream().mapToDouble(Sale::amount).sum();
List<Sale> biggest = sales.stream()
.sorted(Comparator.comparingDouble(Sale::amount).reversed())
.limit(2)
.toList();
Group by — Java’s pivot table
Map<String, Double> byRegion = sales.stream()
.collect(Collectors.groupingBy(Sale::region,
Collectors.summingDouble(Sale::amount)));
// {North=61584.0, South=17970.0, East=16794.0}
💡 Streams are lazy: nothing happens until a terminal operation such as
toList(), sum() or collect() runs. A stream can be consumed only once.toList() needs Java 16+; on older versions use .collect(Collectors.toList()). Reading Excel files in Java? See Apache POI.