Finishing Adapter for Downstream Collectors

The collectingAndThen() method encapsulates a downstream collector and a finisher function to allow the result of the collector to be adapted by the finisher function.

Click here to view code image

static <T,A,R,RR> Collector<T,A,RR> collectingAndThen(
       Collector<T,A,R> downstream,
       Function<R,RR>   finisher)

Returns a Collector that performs the operation of the downstream collector on input elements of type T, followed by applying the finisher function on the result of type R produced by the downstream collector. The final result is of type RR, the result of the finisher function. In other words, the method adapts a collector to perform an additional finishing transformation.

In the call to the collectAndThen() method at (1), the collector Collectors.maxBy() at (2) produces an Optional<Integer> result that is the maximum CD by number of tracks in each group. The finisher function at (3) extracts the value from the Optional<Integer> result, if there is one; otherwise, it returns 0. The collectAndThen() method adapts the Optional<Integer> result of its argument collector to an Integer value by applying the finisher function.

Click here to view code image

Map<Year, Integer> maxTracksByYear = CD.cdList.stream()
    .collect(Collectors.groupingBy(
         CD::year,
         Collectors.collectingAndThen(                                  // (1)
             Collectors.maxBy(Comparator.comparing(CD::noOfTracks)),    // (2)
             optCD -> optCD.map(CD::noOfTracks).orElse(0)))             // (3)
     );
System.out.println(maxTracksByYear);                      // {2017=8, 2018=10}

In the call to the collectAndThen() method at (4), the collector Collectors.averaging-Double() at (5) produces a result of type Double that is the average number of tracks in each group. The finisher function at (6) maps the Double average value to a string with the specified number format.

Click here to view code image

Map<Genre, String> avgTracksByGenre = CD.cdList.stream()
    .collect(Collectors.groupingBy(
         CD::genre,
         Collectors.collectingAndThen(                                  // (4)
             Collectors.averagingDouble(CD::noOfTracks),                // (5)
             d -> String.format(“%.1f”, d)))                            // (6)
     );
System.out.println(avgTracksByGenre);                   // {JAZZ=8.0, POP=9.0}

Downstream Collectors for Functional Reduction

The collectors we have seen so far perform a mutable reduction to some mutable container, except for the functional reduction implemented by the joining() method (p. 984). The Collectors class also provides static factory methods that implement collectors which perform functional reduction to compute common statistics, such as summing, averaging, finding maximum and minimum values, and the like.

Like any other collector, the collectors that perform functional reduction can also be used in a standalone capacity as a parameter of the collect() method and as a downstream collector in a composed collector. However, these collectors are most useful when used as downstream collectors.

Collectors performing functional reduction have counterparts in terminal operations for streams that provide equivalent reduction operations (Table 16.8, p. 1008).

Leave a Reply

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