Course Content
Stream API
Stream API
Aggregating Elements with the reduce() Method
When working with collections in Java, you often need to reduce all elements to a single result, such as a sum, product, or another aggregated value.
There are three variations of the reduce()
method, each designed for different scenarios. Let's go through each one in detail.
reduce() with a Single Parameter
If you need to aggregate elements without an initial value, you can use this version of reduce()
. However, since the stream might be empty, the method returns an Optional<T>
.
This method applies the accumulator
function to all elements in the stream and returns an Optional<T>
.
Practical Example
Imagine an online store with a list of products and their prices. The goal is to calculate the total revenue of all products in the store.
Main
package com.example; import java.util.List; import java.util.Optional; public class Main { public static void main(String[] args) { List<Product> products = List.of( new Product("Laptop", 1200.0), new Product("Mouse", 25.0), new Product("Keyboard", 75.0) ); Optional<Double> totalRevenue = products.stream() .map(Product::getPrice) .reduce(Double::sum); totalRevenue.ifPresent(revenue -> System.out.println("Total revenue: " + revenue)); } } class Product { private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } }
This code creates a list of products, then uses reduce()
to sum up the prices. The map(Product::getPrice)
method extracts the price of each product, while Double::sum
performs the summation. If the result is present, it is printed.
reduce() with Two Parameters
If you want to ensure a guaranteed return value, even when the stream is empty, use the reduce()
method with an identity parameter. The initial value ensures a stable computation.
This version of reduce()
starts with the given identity
value, ensuring that a result is always returned, even for an empty stream.
Practical Example
Suppose a store has a cash register with an initial balance, and you need to add the total price of all products to determine the potential total cash available.
Main
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Product> products = List.of( new Product("Laptop", 1200.0), new Product("Mouse", 25.0), new Product("Keyboard", 75.0) ); double totalRevenue = products.stream() .map(Product::getPrice) .reduce(500.0, Double::sum); System.out.println("Total revenue with initial balance: " + totalRevenue); } } class Product { private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } }
Here, the code extracts prices with map(Product::getPrice)
and applies reduce(500.0, Double::sum)
, where 500.0 represents the initial balance, and Double::sum
adds up the values.
reduce() for Parallel Processing
This version of reduce()
is designed for cases where more complex transformations are required, and results need to be aggregated in parallel.
This method takes three parameters:
identity
– the initial value;accumulator
– a function that transforms each element;combiner
– a function that merges partial results.
Practical Example
In an online store, you need to calculate the total number of characters in all product names. This can be useful for setting limits on receipt length.
Main
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Product> products = List.of( new Product("Laptop", 1200.0), new Product("Mouse", 25.0), new Product("Keyboard", 75.0) ); int totalLength = products.stream() .reduce(0, (sum, product) -> sum + product.getName().length(), Integer::sum); System.out.println("Total name length: " + totalLength); } } class Product { private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } }
The code iterates through the list of products using reduce()
, where 0 is the initial value, (sum, product) -> sum + product.getName().length()
defines the logic for summing the lengths of product names, and Integer::sum
combines the results in a parallel processing environment.
1. What data type does reduce(BinaryOperator<T> accumulator)
return if the stream might be empty?
2. When should you use reduce(T identity, BinaryOperator<T> accumulator)
?
Thanks for your feedback!