Collector / Collectors

Remi Forax forax at univ-mlv.fr
Wed Jun 26 11:42:35 PDT 2013


On 06/26/2013 08:26 PM, Brian Goetz wrote:
> As promised, here are what I see as the two last loose ends in 
> Collector(s), both of which came out of the spec review.
>

[...]

>
> 2.  Making the one-arg reducing(), and minBy/maxBy, return Optional 
> means that queries like "tallest person by city" end up with Optional 
> in the value:
>
>   Map<City, Optional<Person>> m
>     = people.collect(groupingBy(Person::getCity,
> maxBy(comparing(Person::getHeight)));
>
> Which is doubly bad because the optionals here will *always* be 
> present, since otherwise there'd be no associated key.
>
> I can see a few ways to address this:
>  - Provide non-optional versions of minBy/maxBy/reducing, which would 
> probably have to return null for "no elements".
>  - Provide a way to add a finisher to a Collector, which is more 
> general (and could be used, say, to turn toList() into something that 
> always collects to an immutable list.)
>
> The latter could, in turn, be done in one of two ways:
>
>  - Add a andThen(f) method to Collector.  Then the above would read:
>
>    groupingBy(Person::getCity,
>               maxBy(comparing(Person::getHeight))
>                 .andThen(Optional::get))
>
>  - Add a combinator:
>
>    groupingBy(Person::getCity,
>               finishing(maxBy(comparing(Person::getHeight)),
>                         Optional::get)));
>
> I prefer the former because it reads better in usage; using a 
> combinator function feels a little "inside out."

I prefer the former too, what about "thenMap" instead of andThen.

Rémi

>
>
> On 6/25/2013 4:26 PM, Brian Goetz wrote:
>> Here's the review comments we got.  Most have already been dealt with.
>> One additional suggestion for a new Collector (transforming) comes out
>> of this.
>>
>>  From Mike:
>>   - Need extra disclaimers on summingDouble/averagingDouble. Done.
>>
>>  From Sam:
>>   - Fix the examples where things have changed to Optional. Done.  
>> See [1]
>>
>>  From Joe:
>>   - <code> tags leaking into output.  This turns out to be a Javadoc bug
>> when you have a @link to a class that is not present.  Will not appear
>> in final output.
>>   - Example section repeated between Collector and Collectors. I think
>> this is OK; users may land on either first, and I think these examples
>> are important.  Sadly Javadoc has no normal form.
>>   - General spec nits (some addressed already, others will be addressed
>> in final pass).
>>
>>  From Remi:
>>   - I don't understand how to create a Collector instead of forEach in
>> this case:
>>      ConcurrentHashMap<String, LongAdder> map
>>          = new ConcurrentHashMap<>();
>>      Arrays.stream(args).parallel()
>>            .forEach( arg -> map.computeIfAbsent(arg, k -> new
>> LongAdder()).increment() );  See [2]
>>
>>
>> [1] After changing the example:
>>
>>   *     Map<Department, Employee> highestPaidByDept
>>   *         = employees.stream()
>>   * .collect(Collectors.groupingBy(Employee::getDepartment,
>>   * Collectors.maxBy(Comparators.comparing(Employee::getSalary))));
>>
>> to
>>
>>   *     Map<Department, Optional<Employee>> highestPaidByDept
>>   *         = employees.stream()...
>>
>> this suggests we might want a combinator to add a post-function to an
>> existing Collector, such as:
>>
>>    <T,A,R,U> Collector<T,A,U>
>>              transforming(Collector<T,A,R> collector,
>>                           Function<R,U> transformer)
>>
>> that can be used in the downstream of a groupingBy to transform away the
>> Optional at the end:
>>
>>    groupingBy(Employee::getDepartment,
>>               transforming(maxBy(...), Optional::get))
>>
>> THis way, the user can get a Map<Department, Employee> rather than a
>> Map<Departhment, Optional<Employee>>.
>>
>>
>> [2] This can be done, but it involves boxing.
>>
>> Collector<Long, LongAdder, LongAdder> c =
>>    Collectors.of(LongAdder::new,
>>                  (a, v) -> a.increment(),
>>                  (a, b) -> /* merge LongAdder tables by key */);
>>
>> ConcurrentMap<String, LongAdder> map
>>     = stream.collect(groupingByConcurrent(Function.identity(), c));
>>
>>
>>
>> On 6/25/2013 4:01 PM, Brian Goetz wrote:
>>> I've closed the survey.  You can see responses here:
>>>
>>> https://www.surveymonkey.com/sr.aspx?sm=fAIHxonslEapeh3et_2biuRHRGUer22Q8awZI1yaJrsT0_3d 
>>>
>>>
>>>
>>>
>>> I'll be summarizing and responding to these soon.
>>>
>>> On 6/18/2013 11:58 AM, Brian Goetz wrote:
>>>> I think I may (finally!) have a final API candidate for Collector /
>>>> Collectors.  Updated docs here:
>>>>
>>>>    http://cr.openjdk.java.net/~briangoetz/doctmp/doc/
>>>>
>>>> Salient recent changes:
>>>>   - Incorporation of finishing function into Collector
>>>>   - Renamings in Collector API
>>>>   - Morph toStringBuilder, toStringJoiner into joining()
>>>>   - Add prefix/suffix version of joining()
>>>>   - Addition of summingBy{Int,Long,Double}
>>>>   - Addition of averagingBy{Int,Long,Double}
>>>>   - Rename toXxxSummaryStatistics to summarizingXxx
>>>>   - no-see reducing, and minBy/maxBy collectors return Optional
>>>>   - elimination of canned merger functions from API
>>>>
>>>> Please review the API and spec.  I have set up a SurveyMonkey with the
>>>> usual password to collect review comments at:
>>>>
>>>>    https://www.surveymonkey.com/s/3TTBJ7K
>>>>
>>>>
>>



More information about the lambda-libs-spec-experts mailing list