Collectors.converting

Peter Levart peter.levart at gmail.com
Wed Jan 30 08:22:34 UTC 2019



On 1/29/19 9:52 PM, Brian Goetz wrote:
> How is this different from Collectors.collectingAndThen?

Hi Brian,

It is exactly the same!

I'm sorry, I haven discovered that method when I needed it. Perhaps I 
was looking for another name?

Regards, Peter

>
> On 1/29/2019 3:30 PM, Peter Levart wrote:
>> Hi,
>>
>> 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 following:
>>
>>     /**
>>      * 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 
>> java.util.stream.Collector.Characteristics#IDENTITY_FINISH}.
>>      *
>>      * @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>() {
>>             @Override
>>             public Supplier<A> supplier() { return 
>> collector.supplier(); }
>>
>>             @Override
>>             public BiConsumer<A, T> accumulator() { return 
>> collector.accumulator(); }
>>
>>             @Override
>>             public BinaryOperator<A> combiner() { return 
>> collector.combiner(); }
>>
>>             @Override
>>             public Function<A, RR> finisher() { return 
>> resultConverter.compose(collector.finisher()); }
>>
>>             @Override
>>             public Set<Characteristics> characteristics() {
>>                 EnumSet<Characteristics> characteristics = 
>> EnumSet.noneOf(Characteristics.class);
>> characteristics.addAll(collector.characteristics());
>> characteristics.remove(Characteristics.IDENTITY_FINISH);
>>                 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 =
>>             measurements
>>                 .collect(
>>                     groupingBy(
>>                         Measurement::type,
>>                         summarizingLong(Measurement::value)
>>                     )
>>                 );
>>
>> ...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())
>>                              .divide(
>> BigDecimal.valueOf(stats.getCount()),
>>                                  20, RoundingMode.HALF_EVEN);
>>         }
>>
>>         Map<String, BigDecimal> averageByType =
>>             statsByType
>>                 .entrySet()
>>                 .stream()
>>                 .collect(toMap(Map.Entry::getKey, e -> 
>> toBDAverage(e.getValue())));
>>
>> ...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 =
>>             measurements
>>                 .collect(
>>                     groupingBy(
>>                         Measurement::type,
>>                         converting(
>>                             summarizingLong(Measurement::value),
>>                             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