JEP 186: Collection Literals
Howard Lovatt
howard.lovatt at gmail.com
Wed Jan 15 15:24:23 PST 2014
I don't like limited solutions that are reserved for the language and would
therefore prefer an exploration of general solutions to the problem. In
this particular case, won't we be using Streams anyway :). Therefore
solutions for Lists etc. are past their use by date :).
I think there are two categories for a generic solution, (1) use a static
factory method or (2) use a builder class. The following example (syntax
arbitrary) illustrates the issues and relative strengths of the two
approaches:
IntStream is = {1, 2, 3};
This could be translated using a static factory (1) into:
IntStream is = IntStream.of(1, 2, 3);
Or using a Builder class (2) into:
IntStream is = IntStream.builder().add(1).add(2).add(3).build();
In both cases you would have to be careful not to end up excessive object
creation. But it is manageable, for static factory (1) you would do
something like IntStream actually does:
public interface IntStream {
static IntStream of(int ... values) { return /* an IntStream that uses
the array values directly without copying it */ }
...
}
This works OK for structures whose internal representation is an array, but
is a problem if you don't want an array. It is more complicated for a
builder, but possible:
public interface IntStream {
static IntStream.Builder builder() { return new IntStreamImpl(); }
...
}
class IntStreamImpl implements IntStream, IntStream.Builder { // Class is
both the builder and the stream
public IntStream build() { return this; }
...
}
The *trick* above is that the implementation is mutable, it is both an
IntStream and an IntStream.Builder, therefore there is only one class
created.
Because the static factory (1) is biased towards the internal
representation as an array I would favour option (2), translate the literal
into a builder.
A further useful test is how well this works for nested structures (I am
sticking with streams but Map would be another example):
Stream<IntStream> sis = {{1, 2}, {4, 5}}; // a Map<Integer, Integer>
literal would look the same as this
The translation to a builder would be:
Stream<IntStream> sis =
Stream.builder().add(IntStream.builder().add(1).add(2).build()).add(IntStream.builder().add(4).add(5).build()).build();
With the popularity of FLUENT style APIs, is the type inference good enough
for:
int total = {1, 2, 3}.sum(); // ?
Which would be translated into:
int total = IntStream.builder().add(1).add(2).add(3).build().sum(); // IE
can it work out that it needs an IntStream - my guess is no :(
-- Howard.
On 16 January 2014 04:28, Per Bothner <per at bothner.com> wrote:
> On 01/15/2014 09:14 AM, Remi Forax wrote:
> > On 01/15/2014 06:04 PM, Per Bothner wrote:
> >> (1) Target-typing means you don't have to redundantly specify T:
> >>
> >> T v = { e1, ..., en};
> >>
> >> vs
> >>
> >> T v = T.of(e1, ..., en);
> >>
> >> (2) Using the T.of form requires allocating an array,
> >> which is then thrown away.
> >>
> >
> > I disagree for (1),
>
> You mean (2), presumably.
>
> > if you take a look to java.util.EnumSet by example,
> > you will see that there are multiple overloads of 'of' to avoid to
> allocate
> > an array in the common cases.
>
> For EnumSet, the short cases are probably the common cases.
> That doesn't help for lists that are longer than some small number.
>
> However, I don't consider this a major motivation. If performance
> is a factor, it should be possible for the VM to special-case
> the argument-passing. Certainly for common well-known classes.
> --
> --Per Bothner
> per at bothner.com http://per.bothner.com/
>
>
--
-- Howard.
More information about the lambda-dev
mailing list