## 9.2.  Implement decomposition and reduction with streams [CORE_JAVA9_IMPATIENT] Chapter 8. Section 12. Reduction Operations.

Reduction

The `reduce(T identity, BinaryOperator<T> accumulator)` method of the `Stream` interface performs a reduction operation on the stream to reduce the stream to a single value. It takes an initial value and an accumulator that is a `BinaryOperator<T>` as arguments. The first time, the accumulator receives the initial value and the first element of the stream as arguments, and returns a value. The second time, the accumulator receives the value returned from its previous call and the second element from the stream. This process continues until all elements of the stream have been passed to the accumulator.

The returned value from the last call of the accumulator is returned from the `reduce(...)` method. The following snippet of code performs the summation of all integers in the stream:

```
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);
Integer sum = numbers.stream().reduce(0, (n1, n2) -> n1 + n2); // must be 36
System.out.print("Sum of all integers in the stream : " + sum);

```

The `Integer` class contains a static `sum(...)` method to perform sum of two integers. You can rewrite the code using a method reference, like so:

```Integer sum = numbers.stream().reduce(0, Integer::sum);
```

Reduction operations

A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list. The streams classes have multiple forms of general reduction operations, called `reduce()` and `collect()`, as well as multiple specialized reduction forms such as `sum()`, `max()`, or `count()`.

Of course, such operations can be readily implemented as simple sequential loops, as in:

```int sum = 0;
for (int x : numbers) {
sum += x;
}
```

However, there are good reasons to prefer a reduce operation over a mutative accumulation such as the above. Not only is a reduction "more abstract" -- it operates on the stream as a whole rather than individual elements -- but a properly constructed reduce operation is inherently parallelizable, so long as the function(s) used to process the elements are associative and stateless. For example, given a stream of numbers for which we want to find the sum, we can write:

```int sum = numbers.stream().reduce(0, (x, y) -> x + y);
```

or:

```int sum = numbers.stream().reduce(0, Integer::sum);
```

These reduction operations can run safely in parallel with almost no modification:

```int sum = numbers.parallelStream().reduce(0, Integer::sum);
```

Reduction parallellizes well because the implementation can operate on subsets of the data in parallel, and then combine the intermediate results to get the final correct answer. (Even if the language had a "parallel for-each" construct, the mutative accumulation approach would still required the developer to provide thread-safe updates to the shared accumulating variable sum, and the required synchronization would then likely eliminate any performance gain from parallelism.) Using `reduce()` instead removes all of the burden of parallelizing the reduction operation, and the library can provide an efficient parallel implementation with no additional synchronization required.

In its more general form, a reduce operation on elements of type <T> yielding a result of type <U> requires three parameters:

```
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);

```

• The identity element is both an initial seed value for the reduction and a default result if there are no input elements.

• The accumulator function takes a partial result and the next element, and produces a new partial result.

• The combiner function combines two partial results to produce a new partial result. (The combiner is necessary in parallel reductions, where the input is partitioned, a partial accumulation computed for each partition, and then the partial results are combined to produce a final result.)

The code to sum all the integers in a stream can be rewritten as follows:

```
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);
Integer sum = numbers.stream().reduce(0,
(res, i) -> res + i,                  // adds one element's value to partial result
(part1, part2) -> part1 + part2);     // combine two partial results
System.out.print("Sum of all integers in the stream : " + sum);

```    