Parallel Functional Reduction

Parallel execution is illustrated in Figure 16.12. Multiple instances of the stream pipeline are executed in parallel, where each pipeline instance processes a segment of the stream. In this case, only one CD is allocated to each pipeline instance. Each pipeline instance thus produces its partial sum, and the combiner is applied in parallel on the partial sums to combine them into a final result. No additional synchronization is required to run the reduce() operation in parallel.

Figure 16.12 also illustrates why the initial value must be an identity value. Say we had specified the initial value to be 3. Then the value 3 would be added multiple times to the sum during parallel execution. We also see why both the accumulator and the combiner are associative, as this allows for any two values to be combined in any order.

Figure 16.12 Parallel Functional Reduction

When the single-argument and two-argument reduce() methods are applied to a parallel stream, the accumulator also acts as the combiner. The three-argument reduce() method can usually be replaced with a map-reduce transformation, making the combiner redundant.

We conclude the discussion on implementing functional reductions by implementing the max() method that finds the maximum element in a stream according to a given comparator. A comparator that compares by the CD title is defined at (26). A binary operator that finds the maximum of two CDs when compared by title is defined at (27). It uses the comparator defined at (26). The stream pipeline at (28) finds the maximum of all jazz music CDs by title. The method calls at (29a), (29b), and (29c) are equivalent.

Click here to view code image

Comparator<CD> cmpByTitle = Comparator.comparing(CD::title);    // (26)
BinaryOperator<CD> maxByTitle =
    (cd1, cd2) -> cmpByTitle.compare(cd1, cd2) > 0 ? cd1 : cd2; // (27)
Optional<CD> optMaxJazzCD = CD.cdList                  // (28)
    .stream()
    .filter(CD::isJazz)
    .reduce(BinaryOperator.maxBy(cmpByTitle));         // (29a)
//  .reduce(maxByTitle);                               // (29b)
//  .max(cmpByTitle);                                  // (29c)
optMaxJazzCD.map(CD::title).ifPresent(out::println);   // Keep on Erasing

The accumulator at (29a), returned by the BinaryOperator.maxBy() method, will compare the previous maximum CD and the current CD by title to compute a new maximum jazz music CD. The accumulator used at (29b) is implemented at (27). It also does the same comparison as the accumulator at (29a). At (29c), the max() method also does the same thing, based on the comparator at (26). Note that the return value is an Optional<CD>, as the stream might be empty. The Optional<CD> is mapped to an Optional<String>. If it is not empty, its value—that is, the CD title— is printed.

The reduce() method does not terminate if applied to an infinite stream, as the method will never finish processing all stream elements.

Leave a Reply

Your email address will not be published. Required fields are marked *