Reduction OperationsS2C Home « Reduction Operations

Lets start by explaining what reduction operations are! A reduction operation, which is also called a fold in functional programming parlance, takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation. This could be counting the number of elements in a stream, summing some numbers or finding the maximum value.

We have already encountered the general reduction operations collect() and reduce() in previous lessons and we have also used the specialised reduction operation count(). As you may have inferred from the methods mentioned so far, all reduction operations are terminal.

In this lesson we take a much closer look at all these methods as well other reduction operations methods we haven't used yet. We will start by looking at general reduction operations.

General Reduction Operations Top

There are two general reduction operations, the collect() method which performs a mutable reduction operation on the elements of this stream, optionally using a Collector and the reduce() method which in its simplest form performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.

A mutable reduction is one where the reduced value is a mutable result container e.g. a HashSet<E>, and elements are incorporated by updating the state of the result rather than by replacing the result

Lets try these methods out! We will be using the Employee class we created in the Introducing Streams lesson to test against for this purpose.

Following is a TestGeneralReduction class to demonstrate use of the collect() and reduce() methods of the Stream<E> interface.


package com.server2client;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;

/* General reduction operations */
public class TestGeneralReduction {

    public static void main(String[] args) {

        /* Box and collect to list */
        List<Double> doubles = DoubleStream.of(16.2, 8.06, 3.0001, 12, 16.16666)
                .boxed()
                .collect(Collectors.toList());
        System.out.println("Doubles in List collection: " + doubles);

        /* Get total salary of all staff */
        Optional<Double> salary = Employee.listOfStaff().stream()
                .map(Employee::getSalary)
                .reduce((e1, e2) -> e1 + e2);
        System.out.println("All Employee salary total is " + salary);

        /* Get minimum age of all staff */
        Optional<Integer> minAge = Employee.listOfStaff().stream()
                .map(Employee::getAge)
                .reduce(Integer::min);
        System.out.println("Youngest member of staff is " + minAge);

        /* Get maximum age of all staff */
        int maxAge = Employee.listOfStaff().stream()
                .map(Employee::getAge)
                .reduce(0, Integer::max);
        System.out.println("Oldest member of staff is " + maxAge);
    }
}

Building and running the TestGeneralReduction class produces the following output:

Run TestGeneralReduction class
Screenshot 1. Running the TestGeneralReduction class.

Lets go through the code.

We can't store primitives in collections as they are generic so we use the handy boxed() method to wrap some doubles and use the collect method along with Collectors.toList() to store the results in a List<E> which we print off. We look at collectors in more detail in the Stream Collectors lesson.

In the first use of reduce() we are using the variant that only takes one parameter, we map and total up the salary of all staff. This could have been more succinctly expressed as .reduce(Double::sum)

In the second use of reduce() we are again using the variant that only takes one parameter, we map the age and find the youngest employee.

In the third use of reduce() we are using the variant that takes two parameters, where the first is a default value for when the stream is empty, we map the age and find the eldest employee.

The reason the one parameter variant of reduce() returns an Optional is to remove NullPointerException scenarios.

You won't generally use the reduce() method, its a lot more efficient to use the aggregate fucntions of the primitive streams DoubleStream, IntStream and LongStream, such as mapToInt(), which don't require boxing.

Specialised Reduction Operations Top

There are three specialised reduction operations, the count() method which returns the count of elements in this stream, the min() method which returns the minimum element of this stream according to the provided Comparator and the max() method returns the maximum element of this stream according to the provided Comparator.

Lets try these methods out! Once again we will be using the Employee class we created in the Introducing Streams lesson to test against for this purpose.

Following is a TestSpecialisedReduction class to demonstrate use of the count(), min() and max() methods of the Stream<E> interface.


package com.server2client;

import java.util.Optional;

import static java.util.Comparator.comparing;

/* Specialised reduction operations */
public class TestSpecialisedReduction {

    public static void main(String[] args) {

        /* Get count of people over 50 */
        long count = Employee.listOfStaff().stream()
                .filter(e -> e.getAge() > 50)
                .count();
        System.out.println("Total employees: " + count);

        /* Get minimum salary of all staff */
        Optional<Employee> salary = Employee.listOfStaff().stream()
                .min(comparing(Employee::getSalary));
        System.out.println("Lowest salary: " + salary);

        /* Get maximum age of all staff */
        Optional<Employee> maxAge = Employee.listOfStaff().stream()
                .max(comparing(Employee::getAge));
        System.out.println("Oldest member of staff: " + maxAge);
    }
}

Building and running the TestSpecialisedReduction class produces the following output:

Run TestSpecialisedReduction class
Screenshot 2. Running the TestSpecialisedReduction class.

First we filter people over 50 and then total them using the count() method.

Then we get the minimum salary of all staff by comparing salaries using the min() method which takes a comparator parameter.

Then we get the maximum age of all staff by comparing ages using the max() method which takes a comparator parameter. In this case we are using the comparing()static method of the Comparator class.

These methods are more efficient than using reduce() which could be used instead.

Related Quiz

Streams Quiz 8 - Reduction Operations Quiz

Lesson 8 Complete

In this lesson we looked at stream reduction operations.

What's Next?

In the next lesson we look at stream collectors.