Reducing
Collectors that perform common statistical operations, such as counting, averaging, and so on, are special cases of functional reduction that can be implemented using the Collectors.reducing() method.
static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> bop)
Returns a collector that performs functional reduction, producing an Optional with the cumulative result of applying the binary operator bop on the input elements: e1 bop e2 bop e3 …, where each ei is an input element. If there are no input elements, an empty Optional<T> is returned.
Note that the collector reduces input elements of type T to a result that is an Optional of type T.
static <T> Collector<T,?,T> reducing(T identity, BinaryOperator<T> bop)
Returns a collector that performs functional reduction, producing the cumulative result of applying the binary operator bop on the input elements: identity bop e1 bop e2 …, where each ei is an input element. The identity value is the initial value to accumulate. If there are no input elements, the identity value is returned.
Note that the collector reduces input elements of type T to a result of type T.
static <T,U> Collector<T,?,U> reducing(
U identity,
Function<? super T,? extends U> mapper,
BinaryOperator<U> bop)
Returns a collector that performs a map-reduce operation. It maps each input element of type T to a mapped value of type U by applying the mapper function, and performs functional reduction on the mapped values of type U by applying the binary operator bop. The identity value of type U is used as the initial value to accumulate. If the stream is empty, the identity value is returned.
Note that the collector reduces input elements of type T to a result of type U.
Collectors returned by the Collectors.reducing() methods effectively perform equivalent functional reductions as the reduce() methods of the stream interfaces. However, the three-argument method Collectors.reducing(identity, mapper, bop) performs a map-reduce operation using a mapper function and a binary operator bop, whereas the Stream.reduce(identity, accumulator, combiner) performs a reduction using an accumulator and a combiner (p. 955). The accumulator is a BiFunction<U,T,U> that accumulates a partially accumulated result of type U with an element of type T, whereas the bop is a BinaryOperator<U> that accumulates a partially accumulated result of type U with an element of type U.
The following comparators are used in the examples below:
// Comparator to compare CDs by title.
Comparator<CD> cmpByTitle = Comparator.comparing(CD::title); // (1)
// Comparator to compare strings by their length.
Comparator<String> byLength = Comparator.comparing(String::length); // (2)
The collector returned by the Collectors.reducing() method is used as a standalone collector at (3) to find the CD with the longest title. The result of the operation is an Optional<String> as there might not be any input elements. This operation is equivalent to using the Stream.reduce() terminal operation at (4).
Optional<String> longestTitle1 = CD.cdList.stream()
.map(CD::title)
.collect(Collectors.reducing(
BinaryOperator.maxBy(byLength))); // (3) Standalone collector
System.out.println(longestTitle1.orElse(“No title”));// Keep on Erasing
Optional<String> longestTitle2 = CD.cdList.stream() // Stream<CD>
.map(CD::title) // Stream<String>
.reduce(BinaryOperator.maxBy(byLength)); // (4) Stream.reduce(bop)
The collector returned by the one-argument Collectors.reducing() method is used as a downstream collector at (5) to find the CD with the longest title in each group classified by the year a CD was released. The collector at (5) is equivalent to the collector returned by the Collectors.maxBy(cmpByTitle) method.
Map<Year, Optional<CD>> cdWithMaxTitleByYear = CD.cdList.stream()
.collect(Collectors.groupingBy(
CD::year,
Collectors.reducing( // (5) Downstream collector
BinaryOperator.maxBy(cmpByTitle))
));
System.out.println(cdWithMaxTitleByYear);
// {2017=Optional[<Jaav, “Java Jive”, 8, 2017, POP>],
// 2018=Optional[<Funkies, “Lambda Dancing”, 10, 2018, POP>]}
System.out.println(cdWithMaxTitleByYear.get(Year.of(2018))
.map(CD::title).orElse(“No title”)); // Lambda Dancing
The collector returned by the three-argument Collectors.reducing() method is used as a downstream collector at (6) to find the longest title in each group classified by the year a CD was released. Note that the collector maps a CD to its title. The longest title is associated with the map value for each group classified by the year a CD was released. The collector will return an empty string (i.e., the identity value “”) if there are no CDs in the stream. In comparison, the collector Collectors.mapping() at (7) also maps a CD to its title, and uses the downstream collector Collectors.maxBy(byLength) at (7) to find the longest title (p. 993). The result in this case is an Optional<String>, as there might not be any input elements.
Map<Year, String> longestTitleByYear = CD.cdList.stream()
.collect(Collectors.groupingBy(
CD::year,
Collectors.reducing(“”, CD::title, // (6) Downstream collector
BinaryOperator.maxBy(byLength))
));
System.out.println(longestTitleByYear); // {2017=Java Jive, 2018=Keep on Erasing}
System.out.println(longestTitleByYear.get(Year.of(2018))); // Keep on Erasing
Map<Year, Optional<String>> longestTitleByYear2 = CD.cdList.stream()
.collect(Collectors.groupingBy(
CD::year,
Collectors.mapping(CD::title, // (7) Downstream collector
Collectors.maxBy(byLength))
));
System.out.println(longestTitleByYear2);
// {2017=Optional[Java Jive], 2018=Optional[Keep on Erasing]}
System.out.println(longestTitleByYear2.get(Year.of(2018))
.orElse(“No title.”)); // Keep on Erasing
The pipeline below groups CDs according to the year they were released. For each group, the collector returned by the three-argument Collectors.reducing() method performs a map-reduce operation at (8) to map each CD to its number of tracks and accumulate the tracks in each group. This map-reduce operation is equivalent to the collector returned by the Collectors.summingInt() method at (9).
Map<Year, Integer> noOfTracksByYear = CD.cdList.stream()
.collect(Collectors.groupingBy(
CD::year,
Collectors.reducing( // (8) Downstream collector
0, CD::noOfTracks, Integer::sum)));
System.out.println(noOfTracksByYear); // {2017=14, 2018=28}
System.out.println(noOfTracksByYear.get(Year.of(2018)));// 28
Map<Year, Integer> noOfTracksByYear2 = CD.cdList.stream()
.collect(Collectors.groupingBy(
CD::year,
Collectors.summingInt(CD::noOfTracks))); // (9) Special case collector