Peter Levart peter.levart at
Tue Jan 29 20:30:37 UTC 2019


I wonder if there's any interest in adding a convenience factory method 
for a Collector to the standard repertoire which would look like the 

      * Convert given {@link Collector} so that it applies an additional 
finisher function that
      * converts the result of given collector. The characteristics of 
returned collector is
      * the same as that of the given inner collector but without any
      * {@link}.
      * @param collector       the inner collector to delegate collection to
      * @param resultConverter the additional result finisher function
      * @param <T>             the type of input stream elements
      * @param <A>             the type of intermediate aggregation
      * @param <R>             the type of result of inner collector
      * @param <RR>            the type of final result
      * @return the converted collector
     public static <T, A, R, RR> Collector<T, A, RR> converting(
         Collector<T, A, R> collector,
         Function<? super R, RR> resultConverter
     ) {
         return new Collector<T, A, RR>() {
             public Supplier<A> supplier() { return collector.supplier(); }

             public BiConsumer<A, T> accumulator() { return 
collector.accumulator(); }

             public BinaryOperator<A> combiner() { return 
collector.combiner(); }

             public Function<A, RR> finisher() { return 
resultConverter.compose(collector.finisher()); }

             public Set<Characteristics> characteristics() {
                 EnumSet<Characteristics> characteristics = 
                 return Collections.unmodifiableSet(characteristics);

The rationale is as follows... Composability of collectors allows doing 
things like:

         interface Measurement {
             long value();
             String type();

         Stream<Measurement> measurements = ....

         Map<String, LongSummaryStatistics> statsByType =

...but say I wanted the final result to be a map of avarage values by 
type and the values to be BigDecimal objects calculated with scale of 
19. I can create an intermediate map like above and then transform it to 
new map, like this:

         static BigDecimal toBDAverage(LongSummaryStatistics stats) {
             return BigDecimal.valueOf(stats.getSum())
                                  20, RoundingMode.HALF_EVEN);

         Map<String, BigDecimal> averageByType =
                 .collect(toMap(Map.Entry::getKey, e -> 

...this is ugly as it requires intermediate result to be materialized as 
a HashMap. Wouldn't it be possible to collect the original stream to 
final result in one go?

With the above Collectors.converting factory method, it would:

         Map<String, BigDecimal> averageByType =
                             stats -> toBDAverage(stats)

We already have Collectors.mapping(Function, Collector) method that 
constructs Collector that maps elements to be collected by inner 
collector. Collectors.converting(Collectors, Function) would be a twin 
brother that constructs Collector that converts the collection result of 
the inner collector. Both are useful in compositions like above, but we 
only have the 1st...

What do you think?

Regards, Peter

More information about the core-libs-dev mailing list