The reduce() operation combines all elements in a stream to produce a single value.
The reduce operation takes two parameters called a seed (initial value) and an accumulator.
The accumulator is a function. If the stream is empty, the seed is the result.
The seed and an element are passed to the accumulator, which returns partial result. And then the partial result and the next element are passed to the accumulator function.
This repeats until all elements are passed to the accumulator. The last value returned from the accumulator is the result of the reduce operation.
The stream-related interfaces contain two methods called reduce() and collect() to perform generic reduce operations.
Methods such as sum(), max(), min(), count(), are defined in IntStream, LongStream, and DoubleStream interfaces.
count() method is available for all types of streams.
The Stream<T> interface contains a reduce() method to perform the reduce operation. The method has three overloaded versions:
T reduce(T identity, BinaryOperator<T> accumulator) <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) Optional<T> reduce(BinaryOperator<T> accumulator)
The first version of the reduce() method takes an identity and an accumulator as arguments and reduces the stream to a single value of the same type.
import java.util.Arrays; import java.util.List; // ww w. j av a 2 s. c o m public class Main { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream() .reduce(0, Integer::sum); System.out.println(sum); } }
The code above generates the following result.
To compute the sum of the incomes of all employee.
import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.List; /*from ww w . jav a 2s . com*/ public class Main { public static void main(String[] args) { double sum = Employee.persons() .stream() .map(Employee::getIncome) .reduce(0.0, Double::sum); System.out.println(sum); } } class Employee { public static enum Gender { MALE, FEMALE } private long id; private String name; private Gender gender; private LocalDate dob; private double income; public Employee(long id, String name, Gender gender, LocalDate dob, double income) { this.id = id; this.name = name; this.gender = gender; this.dob = dob; this.income = income; } public double getIncome() { return income; } public static List<Employee> persons() { Employee p1 = new Employee(1, "Jake", Gender.MALE, LocalDate.of(1971, Month.JANUARY, 1), 2343.0); Employee p2 = new Employee(2, "Jack", Gender.MALE, LocalDate.of(1972, Month.JULY, 21), 7100.0); Employee p3 = new Employee(3, "Jane", Gender.FEMALE, LocalDate.of(1973, Month.MAY, 29), 5455.0); Employee p4 = new Employee(4, "Jode", Gender.MALE, LocalDate.of(1974, Month.OCTOBER, 16), 1800.0); Employee p5 = new Employee(5, "Jeny", Gender.FEMALE, LocalDate.of(1975, Month.DECEMBER, 13), 1234.0); Employee p6 = new Employee(6, "Jason", Gender.MALE, LocalDate.of(1976, Month.JUNE, 9), 3211.0); List<Employee> persons = Arrays.asList(p1, p2, p3, p4, p5, p6); return persons; } }
The code above generates the following result.
The second version of the reduce method shown as follows allows us to perform a map operation, followed by a reduce operation.
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
The third argument is used for combining the partial results when the reduce operation is performed in parallel.
import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.List; // w w w .j a v a 2 s.c o m public class Main { public static void main(String[] args) { double sum = Employee.persons() .stream() .reduce(0.0, (partialSum, person) -> partialSum + person.getIncome(), Double::sum); System.out.println(sum); } } class Employee { public static enum Gender { MALE, FEMALE } private long id; private String name; private Gender gender; private LocalDate dob; private double income; public Employee(long id, String name, Gender gender, LocalDate dob, double income) { this.id = id; this.name = name; this.gender = gender; this.dob = dob; this.income = income; } public double getIncome() { return income; } public void setIncome(double income) { this.income = income; } public static List<Employee> persons() { Employee p1 = new Employee(1, "Jake", Gender.MALE, LocalDate.of(1971, Month.JANUARY, 1), 2343.0); Employee p2 = new Employee(2, "Jack", Gender.MALE, LocalDate.of(1972, Month.JULY, 21), 7100.0); Employee p3 = new Employee(3, "Jane", Gender.FEMALE, LocalDate.of(1973, Month.MAY, 29), 5455.0); Employee p4 = new Employee(4, "Jode", Gender.MALE, LocalDate.of(1974, Month.OCTOBER, 16), 1800.0); Employee p5 = new Employee(5, "Jeny", Gender.FEMALE, LocalDate.of(1975, Month.DECEMBER, 13), 1234.0); Employee p6 = new Employee(6, "Jason", Gender.MALE, LocalDate.of(1976, Month.JUNE, 9), 3211.0); List<Employee> persons = Arrays.asList(p1, p2, p3, p4, p5, p6); return persons; } }
The code above generates the following result.
Java Streams API supports map reduce operations in parallel.
When using the following reduce method, each thread accumulates the partial results using the accumulator. At the end, the combiner is used to combine the partial results from all threads to get the result.
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
The following code shows how the sequential and parallel reduce operation works.
import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.List; //w w w.ja v a 2 s. co m public class Main { public static void main(String[] args) { double sum = Employee .persons() .stream() .reduce( 0.0, (Double partialSum, Employee p) -> { double accumulated = partialSum + p.getIncome(); System.out.println(Thread.currentThread().getName() + " - Accumulator: partialSum = " + partialSum + ", person = " + p + ", accumulated = " + accumulated); return accumulated; }, (a, b) -> { double combined = a + b; System.out.println(Thread.currentThread().getName() + " - Combiner: a = " + a + ", b = " + b + ", combined = " + combined); return combined; }); System.out.println("--------------------------------------"); System.out.println(sum); sum = Employee .persons() .parallelStream() .reduce( 0.0, (Double partialSum, Employee p) -> { double accumulated = partialSum + p.getIncome(); System.out.println(Thread.currentThread().getName() + " - Accumulator: partialSum = " + partialSum + ", person = " + p + ", accumulated = " + accumulated); return accumulated; }, (a, b) -> { double combined = a + b; System.out.println(Thread.currentThread().getName() + " - Combiner: a = " + a + ", b = " + b + ", combined = " + combined); return combined; }); System.out.println(sum); } } class Employee { public static enum Gender { MALE, FEMALE } private long id; private String name; private Gender gender; private LocalDate dob; private double income; public Employee(long id, String name, Gender gender, LocalDate dob, double income) { this.id = id; this.name = name; this.gender = gender; this.dob = dob; this.income = income; } public double getIncome() { return income; } public static List<Employee> persons() { Employee p1 = new Employee(1, "Jake", Gender.MALE, LocalDate.of(1971, Month.JANUARY, 1), 2343.0); Employee p2 = new Employee(2, "Jack", Gender.MALE, LocalDate.of(1972, Month.JULY, 21), 7100.0); Employee p3 = new Employee(3, "Jane", Gender.FEMALE, LocalDate.of(1973, Month.MAY, 29), 5455.0); Employee p4 = new Employee(4, "Jode", Gender.MALE, LocalDate.of(1974, Month.OCTOBER, 16), 1800.0); Employee p5 = new Employee(5, "Jeny", Gender.FEMALE, LocalDate.of(1975, Month.DECEMBER, 13), 1234.0); Employee p6 = new Employee(6, "Jason", Gender.MALE, LocalDate.of(1976, Month.JUNE, 9), 3211.0); List<Employee> persons = Arrays.asList(p1, p2, p3, p4, p5, p6); return persons; } }
The code above generates the following result.
The third version of the reduce() listed as follows method is used to perform a reduction operation without default value.
reduce(BinaryOperator<T> accumulator)
If the stream is empty, we cannot use default value as 0.
Optional<T>
is used to wrap the result or the absence of a result.
The following code shows how to compute the maximum of integers in a stream:
import java.util.Optional; import java.util.stream.Stream; //from w ww.ja va 2s .c om public class Main { public static void main(String[] args) { Optional<Integer> max = Stream.of(1, 2, 3, 4, 5).reduce(Integer::max); if (max.isPresent()) { System.out.println("max = " + max.get()); } else { System.out.println("max is not defined."); } max = Stream.<Integer> empty().reduce(Integer::max); if (max.isPresent()) { System.out.println("max = " + max.get()); } else { System.out.println("max is not defined."); } } }
The code above generates the following result.
The following code prints the details of the highest earner in the employee's list.
import java.time.LocalDate; import java.time.Month; import java.util.Arrays; import java.util.List; import java.util.Optional; //www.ja v a 2 s . c om public class Main { public static void main(String[] args) { Optional<Employee> person = Employee .persons() .stream() .reduce((p1, p2) -> p1.getIncome() > p2.getIncome() ? p1 : p2); if (person.isPresent()) { System.out.println("Highest earner: " + person.get()); } else { System.out.println("Could not get the highest earner."); } } } class Employee { public static enum Gender { MALE, FEMALE } private long id; private String name; private Gender gender; private LocalDate dob; private double income; public Employee(long id, String name, Gender gender, LocalDate dob, double income) { this.id = id; this.name = name; this.gender = gender; this.dob = dob; this.income = income; } public double getIncome() { return income; } public static List<Employee> persons() { Employee p1 = new Employee(1, "Jake", Gender.MALE, LocalDate.of(1971, Month.JANUARY, 1), 2343.0); Employee p2 = new Employee(2, "Jack", Gender.MALE, LocalDate.of(1972, Month.JULY, 21), 7100.0); Employee p3 = new Employee(3, "Jane", Gender.FEMALE, LocalDate.of(1973, Month.MAY, 29), 5455.0); Employee p4 = new Employee(4, "Jode", Gender.MALE, LocalDate.of(1974, Month.OCTOBER, 16), 1800.0); Employee p5 = new Employee(5, "Jeny", Gender.FEMALE, LocalDate.of(1975, Month.DECEMBER, 13), 1234.0); Employee p6 = new Employee(6, "Jason", Gender.MALE, LocalDate.of(1976, Month.JUNE, 9), 3211.0); List<Employee> persons = Arrays.asList(p1, p2, p3, p4, p5, p6); return persons; } @Override public String toString() { String str = String.format("(%s, %s, %s, %s, %.2f)\n", id, name, gender, dob, income); return str; } }
The code above generates the following result.