Collectors.converting
Brian Goetz
brian.goetz at oracle.com
Wed Jan 30 16:35:05 UTC 2019
Arguably, it could have been exposed as a default method on Collector
(Collector::andThen) -- but we avoided this approach out of concern that
a generic methods in receiver position can interact with type inference
in ways that confuse people.
On 1/30/2019 3:22 AM, Peter Levart wrote:
>
>
> 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