Apart from the for(:) loop at (2) to iterate over all elements of the list and read the number of tracks in each CD at (3), the two necessary steps are:

  • Initialization of the variable sum at (1)
  • The accumulative operation at (4) that is applied repeatedly to compute a new partial result in the variable sum, based on its previous value and the number of tracks in the current CD

The loop-based solution above can be translated to a stream-based solution, as shown in Figure 16.11. All the code snippets can be found in Example 16.11.

Figure 16.11 Reducing with an Initial Value

In Figure 16.11, the stream created at (6) internalizes the iteration over the elements. The mapToInt() intermediate operation maps each CD to its number of tracks at (7)—the Stream<CD> is mapped to an IntStream. The reduce() terminal operation with two arguments computes and returns the total number of tracks:

  • Its first argument at (8) is the identity element that provides the initial value for the operation and is also the default value to return if the stream is empty. In this case, this value is 0.
  • Its second argument at (9) is the accumulator that is implemented as a lambda expression. It repeatedly computes a new partial sum based on the previous partial sum and the number of tracks in the current CD, as evident from

Figure 16.11. In this case, the accumulator is an IntBinaryOperator whose functional type is (int, int) -> int. Note that the parameters of the lambda expression represent the partial sum and the current number of tracks, respectively.

The stream pipeline in Figure 16.11 is an example of a map-reduce transformation on a sequential stream, as it maps the stream elements first and then reduces them. Typically, a filter operation is also performed before the map-reduce transformation.

Each of the following calls can replace the reduce() method call in Figure 16.11, as they are all equivalent:

Click here to view code image

reduce(0, (sum, noOfTracks) -> Integer.sum(sum, noOfTracks))
reduce(0, Integer::sum)               // Method reference
sum()                                 // Special functional reduction, p. 1001.

In Example 16.11, the stream pipeline at (10) prints the actions taken by the accumulator which is now augmented with print statements. The output at (3) shows that the accumulator actions correspond to those in Figure 16.11.

The single-argument reduce() method only accepts an accumulator. As no explicit default or initial value can be specified, this method returns an Optional. If the stream is not empty, it uses the first element as the initial value; otherwise, it returns an empty Optional. In Example 16.11, the stream pipeline at (13) uses the single-argument reduce() method to compute the total number of tracks on CDs. The return value is an OptionalInt that can be queried to extract the encapsulated int value.

Click here to view code image

OptionalInt optSumTracks0 = CD.cdList                       // (13)
    .stream()
    .mapToInt(CD::noOfTracks)
    .reduce(Integer::sum);                                  // (14)
out.println(“Total number of tracks: ” + optSumTracks0.orElse(0));  // 42

Leave a Reply

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