Refactor of Collector interface
Kevin Bourrillion
kevinb at google.com
Fri Feb 8 08:43:35 PST 2013
Oh, it's about performance. I see that now.
Well, if it's possible to just tell us, "Hey, a group-by of 10000 elements
used to incur N bytes of garbage and now causes only M," that's very easy
to know how to react to.
On Fri, Feb 8, 2013 at 8:36 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
> Your subjective sense is accurate, which is why I brought this up. This
> may be an example where is better to depart from the traditional approach.
>
> To your question, it depends what you mean by "purely to do with an
> implementor." Collector *users* are going to be burdened with the
> performance consequences of multiple layers of wrapping/conversion.
>
> The implementation used to be full of alternation between:
>
> interface Foo<T,U> {
> U transform(T t);
> }
>
> class FooAdapter<T,U> {
> FooAdapter(Function<T,U> lambda) { ... }
>
> U transform(T t) { return lambda.apply(t); }
> }
>
> and
>
> Function<T,U> parentTransformer = foo::transform;
>
> and back again, introducing layers of wrapping even when the function is
> not changing across layers.
>
>
>
>
> On 2/8/2013 11:22 AM, Kevin Bourrillion wrote:
>
>> My subjective sense of good Java API design very strongly prefers the
>> "before" picture here, which I see as a lot more "Java-like", so I'm
>> taking a closer look.
>>
>> I assume that the trade-offs we're weighing here are purely to do with
>> what it's like to be a Collector implementor, correct?
>>
>>
>> On Fri, Feb 8, 2013 at 7:25 AM, Brian Goetz <brian.goetz at oracle.com
>> <mailto:brian.goetz at oracle.com**>> wrote:
>>
>> FYI: In a recent refactoring, I changed:
>>
>> public interface Collector<T, R> {
>> R makeResult();
>> void accumulate(R result, T value);
>> R combine(R result, R other);
>> }
>>
>> to
>>
>> public interface Collector<T, R> {
>> Supplier<R> resultSupplier();
>> BiConsumer<R, T> accumulator();
>> BinaryOperator<R> combiner();
>> }
>>
>> Basically, this is a refactoring from typical interface to
>> tuple-of-lambdas. What I found was that there was a lot of
>> adaptation going on, where something would start out as a lambda,
>> we'd wrap it with a Collector whose method invoked the lambda, then
>> take a method reference to that wrapping method and then later wrap
>> that with another Collector, etc. By keeping access to the
>> functions directly, the Collectors code got simpler and less wrappy,
>> since a lot of functions could just be passed right through without
>> wrapping. And a lot of stupid adapter classes went away.
>>
>> While clearly we don't want all interfaces to evolve this way, this
>> is one where *all* the many layers of manipulations are effectively
>> function composition, and exposing the function-ness made that
>> cleaner and more performant. So while I don't feel completely
>> super-great about it, I think its enough of a win to keep.
>>
>>
>>
>>
>> --
>> Kevin Bourrillion | Java Librarian | Google, Inc. |kevinb at google.com
>> <mailto:kevinb at google.com>
>>
>
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
More information about the lambda-libs-spec-observers
mailing list