Hi Michael,

You see, switch2 is on par with explicit for 0 and 1 args.

switch10 probably suffers from to-many-bytecodes-per-method syndrome.

I tried to shorten your switch2 and switch10 methods by delegating to explicit/varargs methods:

  @SafeVarargs
  static <E> List<E> varargs_switch2(E... ea) {
    switch (ea.length) {
      case 0: return explicit();
      case 1: return explicit(ea[0]);
      default: return varargs(ea);
    }
  }

  @SafeVarargs
  static <E> List<E> varargs_switch10(E... ea) {
    switch (ea.length) {
      case 0: return explicit();
      case 1: return explicit(ea[0]);
      case 2: return explicit(ea[0], ea[1]);
      case 3: return explicit(ea[0], ea[1], ea[2]);
      case 4: return explicit(ea[0], ea[1], ea[2], ea[3]);
      case 5: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4]);
      case 6: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
case 7: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5], ea[6]); case 8: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5], ea[6], ea[7]); case 9: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5], ea[6], ea[7], ea[8]); case 10: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5], ea[6], ea[7], ea[8], ea[9]);
      default: return varargs(ea);
    }
  }


... and the results are as follows (JDK8u60, i7-2600K, Linux):


ListOf.explicit_00 avgt 10 2.600 ± 0.022 ns/op ListOf.explicit_01 avgt 10 4.032 ± 0.237 ns/op ListOf.explicit_02 avgt 10 7.255 ± 0.457 ns/op ListOf.explicit_03 avgt 10 7.684 ± 0.485 ns/op ListOf.explicit_04 avgt 10 8.401 ± 0.803 ns/op ListOf.explicit_05 avgt 10 9.001 ± 0.771 ns/op ListOf.explicit_06 avgt 10 9.467 ± 1.008 ns/op ListOf.explicit_07 avgt 10 10.506 ± 0.774 ns/op ListOf.explicit_08 avgt 10 11.218 ± 0.946 ns/op ListOf.explicit_09 avgt 10 12.466 ± 0.735 ns/op ListOf.explicit_10 avgt 10 13.160 ± 1.680 ns/op

ListOf.varargs_00 avgt 10 6.701 ± 0.986 ns/op ListOf.varargs_01 avgt 10 7.244 ± 0.775 ns/op ListOf.varargs_02 avgt 10 9.751 ± 0.931 ns/op ListOf.varargs_03 avgt 10 10.730 ± 1.055 ns/op ListOf.varargs_04 avgt 10 11.615 ± 0.995 ns/op ListOf.varargs_05 avgt 10 12.923 ± 1.057 ns/op ListOf.varargs_06 avgt 10 12.688 ± 0.963 ns/op ListOf.varargs_07 avgt 10 14.509 ± 0.964 ns/op ListOf.varargs_08 avgt 10 15.214 ± 1.613 ns/op ListOf.varargs_09 avgt 10 15.796 ± 0.099 ns/op ListOf.varargs_10 avgt 10 17.381 ± 2.089 ns/op

ListOf.varargs_switch10_00 avgt 10 2.598 ± 0.031 ns/op ListOf.varargs_switch10_01 avgt 10 4.200 ± 0.377 ns/op ListOf.varargs_switch10_02 avgt 10 6.829 ± 0.445 ns/op ListOf.varargs_switch10_03 avgt 10 8.074 ± 1.230 ns/op ListOf.varargs_switch10_04 avgt 10 8.254 ± 0.644 ns/op ListOf.varargs_switch10_05 avgt 10 9.955 ± 1.643 ns/op ListOf.varargs_switch10_06 avgt 10 9.856 ± 1.172 ns/op ListOf.varargs_switch10_07 avgt 10 11.230 ± 1.182 ns/op ListOf.varargs_switch10_08 avgt 10 17.431 ± 2.253 ns/op ListOf.varargs_switch10_09 avgt 10 18.439 ± 2.199 ns/op ListOf.varargs_switch10_10 avgt 10 20.477 ± 2.698 ns/op

ListOf.varargs_switch2_00 avgt 10 2.598 ± 0.031 ns/op ListOf.varargs_switch2_01 avgt 10 4.306 ± 0.461 ns/op ListOf.varargs_switch2_02 avgt 10 9.561 ± 0.978 ns/op ListOf.varargs_switch2_03 avgt 10 10.859 ± 0.596 ns/op ListOf.varargs_switch2_04 avgt 10 11.810 ± 0.812 ns/op ListOf.varargs_switch2_05 avgt 10 13.451 ± 1.819 ns/op ListOf.varargs_switch2_06 avgt 10 12.310 ± 0.106 ns/op ListOf.varargs_switch2_07 avgt 10 15.589 ± 2.335 ns/op ListOf.varargs_switch2_08 avgt 10 15.177 ± 0.963 ns/op ListOf.varargs_switch2_09 avgt 10 18.139 ± 1.968 ns/op ListOf.varargs_switch2_10 avgt 10 16.802 ± 1.193 ns/op


Now switch10 performs on par with explicit up to 7 arguments after which there's a cliff.

switch2 has a cliff where expected, when it starts to delegate to varargs.

More interesting, when profiling with "-prof gc", we can see that:


ListOf.explicit_00:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁶ B/op ListOf.explicit_01:·gc.alloc.rate.norm avgt 10 24.000 ± 0.001 B/op ListOf.explicit_02:·gc.alloc.rate.norm avgt 10 48.000 ± 0.001 B/op ListOf.explicit_03:·gc.alloc.rate.norm avgt 10 56.000 ± 0.001 B/op ListOf.explicit_04:·gc.alloc.rate.norm avgt 10 56.000 ± 0.001 B/op ListOf.explicit_05:·gc.alloc.rate.norm avgt 10 64.000 ± 0.001 B/op ListOf.explicit_06:·gc.alloc.rate.norm avgt 10 64.000 ± 0.001 B/op ListOf.explicit_07:·gc.alloc.rate.norm avgt 10 72.000 ± 0.001 B/op ListOf.explicit_08:·gc.alloc.rate.norm avgt 10 72.000 ± 0.001 B/op ListOf.explicit_09:·gc.alloc.rate.norm avgt 10 80.000 ± 0.001 B/op ListOf.explicit_10:·gc.alloc.rate.norm avgt 10 80.000 ± 0.001 B/op

ListOf.varargs_00:·gc.alloc.rate.norm avgt 10 40.000 ± 0.001 B/op ListOf.varargs_01:·gc.alloc.rate.norm avgt 10 48.000 ± 0.001 B/op ListOf.varargs_02:·gc.alloc.rate.norm avgt 10 72.000 ± 0.001 B/op ListOf.varargs_03:·gc.alloc.rate.norm avgt 10 88.000 ± 0.001 B/op ListOf.varargs_04:·gc.alloc.rate.norm avgt 10 88.000 ± 0.001 B/op ListOf.varargs_05:·gc.alloc.rate.norm avgt 10 104.000 ± 0.001 B/op ListOf.varargs_06:·gc.alloc.rate.norm avgt 10 104.000 ± 0.001 B/op ListOf.varargs_07:·gc.alloc.rate.norm avgt 10 120.000 ± 0.001 B/op ListOf.varargs_08:·gc.alloc.rate.norm avgt 10 120.000 ± 0.001 B/op ListOf.varargs_09:·gc.alloc.rate.norm avgt 10 136.000 ± 0.001 B/op ListOf.varargs_10:·gc.alloc.rate.norm avgt 10 136.000 ± 0.001 B/op

ListOf.varargs_switch10_00:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁶ B/op ListOf.varargs_switch10_01:·gc.alloc.rate.norm avgt 10 24.000 ± 0.001 B/op ListOf.varargs_switch10_02:·gc.alloc.rate.norm avgt 10 48.000 ± 0.001 B/op ListOf.varargs_switch10_03:·gc.alloc.rate.norm avgt 10 56.000 ± 0.001 B/op ListOf.varargs_switch10_04:·gc.alloc.rate.norm avgt 10 56.000 ± 0.001 B/op ListOf.varargs_switch10_05:·gc.alloc.rate.norm avgt 10 64.000 ± 0.001 B/op ListOf.varargs_switch10_06:·gc.alloc.rate.norm avgt 10 64.000 ± 0.001 B/op ListOf.varargs_switch10_07:·gc.alloc.rate.norm avgt 10 72.000 ± 0.001 B/op ListOf.varargs_switch10_08:·gc.alloc.rate.norm avgt 10 120.000 ± 0.001 B/op ListOf.varargs_switch10_09:·gc.alloc.rate.norm avgt 10 136.000 ± 0.001 B/op ListOf.varargs_switch10_10:·gc.alloc.rate.norm avgt 10 136.000 ± 0.001 B/op

ListOf.varargs_switch2_00:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁶ B/op ListOf.varargs_switch2_01:·gc.alloc.rate.norm avgt 10 24.000 ± 0.001 B/op ListOf.varargs_switch2_02:·gc.alloc.rate.norm avgt 10 72.000 ± 0.001 B/op ListOf.varargs_switch2_03:·gc.alloc.rate.norm avgt 10 88.000 ± 0.001 B/op ListOf.varargs_switch2_04:·gc.alloc.rate.norm avgt 10 88.000 ± 0.001 B/op ListOf.varargs_switch2_05:·gc.alloc.rate.norm avgt 10 104.000 ± 0.001 B/op ListOf.varargs_switch2_06:·gc.alloc.rate.norm avgt 10 104.000 ± 0.001 B/op ListOf.varargs_switch2_07:·gc.alloc.rate.norm avgt 10 120.000 ± 0.001 B/op ListOf.varargs_switch2_08:·gc.alloc.rate.norm avgt 10 120.000 ± 0.001 B/op ListOf.varargs_switch2_09:·gc.alloc.rate.norm avgt 10 136.000 ± 0.001 B/op ListOf.varargs_switch2_10:·gc.alloc.rate.norm avgt 10 136.000 ± 0.001 B/op


switch10 allocates exactly the same as explicit up to 7 arguments, after that it seems that JIT doesn't want to optimize away the allocation of the vararg array.


So it looks like that there's no need to burden the public API with explicit argument overloads.

Regards, Peter


On 11/08/2015 12:44 AM, Michael Hixson wrote:
Hi Peter,

I've attached the source code and JMH output with two new tests:

1. varargs_switch2: Has a switch with cases for 0 and 1 that invoke
Collections.emptyList/singletonList.
2. varargs_switch10:  Has a switch with ten cases that look exactly
like the explicit versions.

Both have a default case that falls back to System.arraycopy.

switch2 seems like a more sane implementation and it performs better
for 0 or 1 arguments.  For larger numbers of arguments, they're
roughly equivalent.

Here is the end snippet from the attached results:


Benchmark                   Mode  Cnt   Score   Error  Units
ListOf.explicit_00          avgt   40   2.550 ± 0.008  ns/op
ListOf.explicit_01          avgt   40   6.929 ± 0.100  ns/op
ListOf.explicit_02          avgt   40  15.011 ± 1.410  ns/op
ListOf.explicit_03          avgt   40  16.203 ± 0.396  ns/op
ListOf.explicit_04          avgt   40  16.397 ± 0.505  ns/op
ListOf.explicit_05          avgt   40  18.252 ± 0.229  ns/op
ListOf.explicit_06          avgt   40  18.623 ± 0.499  ns/op
ListOf.explicit_07          avgt   40  20.614 ± 0.231  ns/op
ListOf.explicit_08          avgt   40  20.792 ± 0.259  ns/op
ListOf.explicit_09          avgt   40  22.998 ± 0.350  ns/op
ListOf.explicit_10          avgt   40  23.105 ± 0.346  ns/op
ListOf.varargs_00           avgt   40  11.363 ± 0.131  ns/op
ListOf.varargs_01           avgt   40  20.877 ± 0.440  ns/op
ListOf.varargs_02           avgt   40  20.004 ± 0.130  ns/op
ListOf.varargs_03           avgt   40  24.264 ± 0.091  ns/op
ListOf.varargs_04           avgt   40  24.309 ± 0.120  ns/op
ListOf.varargs_05           avgt   40  28.936 ± 0.503  ns/op
ListOf.varargs_06           avgt   40  28.676 ± 0.052  ns/op
ListOf.varargs_07           avgt   40  33.858 ± 1.409  ns/op
ListOf.varargs_08           avgt   40  33.685 ± 0.763  ns/op
ListOf.varargs_09           avgt   40  37.639 ± 0.207  ns/op
ListOf.varargs_10           avgt   40  39.188 ± 1.577  ns/op
ListOf.varargs_switch10_00  avgt   40   5.575 ± 0.140  ns/op
ListOf.varargs_switch10_01  avgt   40  13.561 ± 0.353  ns/op
ListOf.varargs_switch10_02  avgt   40  20.145 ± 0.328  ns/op
ListOf.varargs_switch10_03  avgt   40  24.632 ± 0.121  ns/op
ListOf.varargs_switch10_04  avgt   40  25.360 ± 0.941  ns/op
ListOf.varargs_switch10_05  avgt   40  28.977 ± 0.336  ns/op
ListOf.varargs_switch10_06  avgt   40  30.471 ± 1.797  ns/op
ListOf.varargs_switch10_07  avgt   40  33.701 ± 0.128  ns/op
ListOf.varargs_switch10_08  avgt   40  33.737 ± 0.357  ns/op
ListOf.varargs_switch10_09  avgt   40  38.638 ± 1.564  ns/op
ListOf.varargs_switch10_10  avgt   40  38.042 ± 0.090  ns/op
ListOf.varargs_switch2_00   avgt   40   2.543 ± 0.006  ns/op
ListOf.varargs_switch2_01   avgt   40   7.069 ± 0.366  ns/op
ListOf.varargs_switch2_02   avgt   40  20.564 ± 0.739  ns/op
ListOf.varargs_switch2_03   avgt   40  24.453 ± 0.362  ns/op
ListOf.varargs_switch2_04   avgt   40  24.706 ± 0.587  ns/op
ListOf.varargs_switch2_05   avgt   40  29.451 ± 0.917  ns/op
ListOf.varargs_switch2_06   avgt   40  28.856 ± 0.126  ns/op
ListOf.varargs_switch2_07   avgt   40  33.300 ± 0.066  ns/op
ListOf.varargs_switch2_08   avgt   40  36.306 ± 3.242  ns/op
ListOf.varargs_switch2_09   avgt   40  40.739 ± 1.837  ns/op
ListOf.varargs_switch2_10   avgt   40  37.876 ± 0.114  ns/op


-Michael

On Sat, Nov 7, 2015 at 7:41 AM, Peter Levart <peter.lev...@gmail.com> wrote:
Hi Michael,

The comparison between explicit and varargs is not fair. Varargs is using
arraycopy, which I think prevents vararg array allocation to be eliminated.
Try to use a switch on varargs array length and then directly reference it's
elements with constant indices for each case and construct list arrays as
you do in explicit methods. Let's see if this performs any better.

Regards, Peter

On Nov 7, 2015 9:43 AM, "Michael Hixson" <michael.hix...@gmail.com> wrote:
(Oops, forgot to cc the mailing list)

Thanks for the explanations, Stuart.  That all sounds reasonable and
makes sense to me.

I have some additional thoughts inline below, because this is
interesting and I can't resist, but you could ignore them and not hurt
any feelings.

I also wrote up some quick benchmarks comparing explicit versus
varargs implementations just to see the impact for myself.  The output
and source code are included at the end of the email.

-Michael


On Fri, Nov 6, 2015 at 10:28 AM, Stuart Marks <stuart.ma...@oracle.com>
wrote:
On 11/6/15 5:12 AM, Michael Hixson wrote:
+     static <E> List<E> of(E... es) {
+         for (E e : es) {
+             Objects.requireNonNull(e);
+         }
+         // NOTE: this can allow a null element to slip through
+         return Collections.unmodifiableList(Arrays.asList(es));
+     }

Even as a skeletal implementation, this one has to be changed to be
truly immutable, right?  It currently returns a view of the (mutable)
argument array rather than new storage.  Sorry for not providing a
proper test:
Good catch! Funnily I had noticed the TOCTOU case that allowed null
elements
in the array to slip through, but not that the array itself was still
modifiable from the outside. Anyway, I'll fix this. No worries about the
test.

Has anyone been able to quantify the advantage of having these
overloads as opposed to having the varargs versions only?  Is it a
matter of performance?

I ask because the overloads seem like warts on the APIs (which is a
shame -- List and Set are such important APIs).  I'm imagining a
future where:

1. We add these overloads for performance gains now.
2. But they're all skeletal implementations that aren't that perfomant
anyway.  Efficient versions don't make it into Java SE 9.  People that
care a lot about performance avoid using these ones.
3. A few years later, varargs performance or some other language / VM
/ compiler-level change renders the overloads obsolete.
Yeah, the overloads seem like warts on the API, though probably
necessary
ones.

At present, and for the forseeable future, varargs calls allocate an
array
on the heap, whereas fixed-args calls do not. I don't know how to
quantify
the difference though. Certainly the cost of allocation and
initialization
is borne in-line. Then there is the cost of collection. Collecting
short-lived objects is cheap (but not free). There is also the
possibility
of escape analysis eliminating the allocation. This seems unlikely to
me;
certainly not something to be relied upon.

The most likely possible future optimization is "frozen arrays," part of
the
"Arrays 2.0" stuff that John Rose has talked about. This is basically
about
immutable arrays. Here, the possibility is to eliminate the defensive
copy,
if the array created to hold the varargs arguments is made immutable.
(This
will require some adjustment on the callee side, as yet unspecified.)
There's still an array, though. And a defensive copy would still have to
be
made if the caller passes an actual array, as opposed to a varargs list.
(Realizing that we're discussing details of a feature that doesn't
exist (frozen arrays)...)

It seems to me that as long as the callee invoked the method with
comma-separated arguments instead of an array, then the callee can
automatically be opted into frozen arrays.  They never had access to
the array box in the first place.

It also seems like the varargs method could defensively call
array.clone() and expect a no-op (return this) implementation if the
array was already frozen, and so both sides could automatically
benefit from frozen arrays without recompilation.  No?

While I can't quantify it, I do think there's an expense to creating the
varargs array, and there is only a possibility to reduce (but not
eliminate)
its cost in future JDK releases. This cost is entirely avoided by
fixed-args
overloads. (There is the cost of cluttering up the API, though.)
I asked "Is it a matter of performance?" because I thought the
justification for similar overloads in other APIs was different.  I
thought EnumSet and Guava (for example) provided the overloads because
@SafeVarargs did not exist at the time, and they didn't want to scare
callers away with those silly warnings.

Put another way:  if the justification for these new overloads is
simply "the other APIs did it", I hope those original motivations are
not being wrongly applied here.  It sounds like this is strictly about
performance, though.

Turning to the skeletal vs. optimized implementation, my plan is
certainly
to ensure that the optimized implementations get into JDK 9. Of course,
plans can change. If the APIs get in without the optimized
implementations,
I think the big attractor will still be the convenience of using these
static factory methods as opposed to conventional code. They're no
slower
than conventional code, and the space consumed is the same. So I think
they'll be popular even if the space efficiency benefits aren't there
initially.
For some reason I thought the optimized implementations had already
been moved out of scope for Java 9.  I'm glad I was wrong!

When the optimized implementations do get in, callers will benefit, even
without recompilation. Thus there is some present value added based on
potential future benefits.

There is always the set of possible future events that cause something
not
to work out, but I think pursuing the approach I've outlined has a good
chance of benefiting the platform in the long term.

s'marks
----------------------------------------

Benchmark           Mode  Cnt   Score   Error  Units
ListOf.explicit_00  avgt   40   2.564 ± 0.007  ns/op
ListOf.explicit_01  avgt   40   7.859 ± 0.022  ns/op
ListOf.explicit_02  avgt   40  15.808 ± 0.338  ns/op
ListOf.explicit_03  avgt   40  19.145 ± 0.978  ns/op
ListOf.explicit_04  avgt   40  18.558 ± 0.314  ns/op
ListOf.explicit_05  avgt   40  23.457 ± 1.069  ns/op
ListOf.explicit_06  avgt   40  21.398 ± 0.255  ns/op
ListOf.explicit_07  avgt   40  25.307 ± 0.672  ns/op
ListOf.explicit_08  avgt   40  24.137 ± 0.376  ns/op
ListOf.explicit_09  avgt   40  27.418 ± 0.560  ns/op
ListOf.explicit_10  avgt   40  26.871 ± 0.506  ns/op
ListOf.varargs_00   avgt   40  13.520 ± 0.177  ns/op
ListOf.varargs_01   avgt   40  23.740 ± 0.346  ns/op
ListOf.varargs_02   avgt   40  23.435 ± 0.321  ns/op
ListOf.varargs_03   avgt   40  29.564 ± 0.744  ns/op
ListOf.varargs_04   avgt   40  29.640 ± 1.329  ns/op
ListOf.varargs_05   avgt   40  34.552 ± 0.639  ns/op
ListOf.varargs_06   avgt   40  34.249 ± 0.476  ns/op
ListOf.varargs_07   avgt   40  40.656 ± 0.589  ns/op
ListOf.varargs_08   avgt   40  39.900 ± 0.595  ns/op
ListOf.varargs_09   avgt   40  45.060 ± 1.098  ns/op
ListOf.varargs_10   avgt   40  44.546 ± 0.816  ns/op

----------------------------------------

package rnd;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.AbstractList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 20)
@Measurement(iterations = 20)
@Fork(2)
public class ListOf {

   private static final String o = "";

   public static void main(String[] args) throws Exception {
     Options options = new OptionsBuilder()
         .include(ListOf.class.getName())
         .build();
     new Runner(options).run();
   }

   @Benchmark public List<String> explicit_00() { return explicit(); }
   @Benchmark public List<String> explicit_01() { return explicit(o); }
   @Benchmark public List<String> explicit_02() { return explicit(o,o); }
   @Benchmark public List<String> explicit_03() { return explicit(o,o,o); }
   @Benchmark public List<String> explicit_04() { return explicit(o,o,o,o);
}
   @Benchmark public List<String> explicit_05() { return
explicit(o,o,o,o,o); }
   @Benchmark public List<String> explicit_06() { return
explicit(o,o,o,o,o,o); }
   @Benchmark public List<String> explicit_07() { return
explicit(o,o,o,o,o,o,o); }
   @Benchmark public List<String> explicit_08() { return
explicit(o,o,o,o,o,o,o,o); }
   @Benchmark public List<String> explicit_09() { return
explicit(o,o,o,o,o,o,o,o,o); }
   @Benchmark public List<String> explicit_10() { return
explicit(o,o,o,o,o,o,o,o,o,o); }

   @Benchmark public List<String> varargs_00() { return varargs(); }
   @Benchmark public List<String> varargs_01() { return varargs(o); }
   @Benchmark public List<String> varargs_02() { return varargs(o,o); }
   @Benchmark public List<String> varargs_03() { return varargs(o,o,o); }
   @Benchmark public List<String> varargs_04() { return varargs(o,o,o,o); }
   @Benchmark public List<String> varargs_05() { return varargs(o,o,o,o,o);
}
   @Benchmark public List<String> varargs_06() { return
varargs(o,o,o,o,o,o); }
   @Benchmark public List<String> varargs_07() { return
varargs(o,o,o,o,o,o,o); }
   @Benchmark public List<String> varargs_08() { return
varargs(o,o,o,o,o,o,o,o); }
   @Benchmark public List<String> varargs_09() { return
varargs(o,o,o,o,o,o,o,o,o); }
   @Benchmark public List<String> varargs_10() { return
varargs(o,o,o,o,o,o,o,o,o,o); }

   static <E> List<E> explicit() {
     return Collections.emptyList();
   }

   static <E> List<E> explicit(E e1) {
     return Collections.singletonList(Objects.requireNonNull(e1));
   }

   static <E> List<E> explicit(E e1, E e2) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4),
         Objects.requireNonNull(e5)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4),
         Objects.requireNonNull(e5),
         Objects.requireNonNull(e6)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4),
         Objects.requireNonNull(e5),
         Objects.requireNonNull(e6),
         Objects.requireNonNull(e7)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E
e8) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4),
         Objects.requireNonNull(e5),
         Objects.requireNonNull(e6),
         Objects.requireNonNull(e7),
         Objects.requireNonNull(e8)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E
e7, E e8, E e9) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4),
         Objects.requireNonNull(e5),
         Objects.requireNonNull(e6),
         Objects.requireNonNull(e7),
         Objects.requireNonNull(e8),
         Objects.requireNonNull(e9)
     });
   }

   static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E
e7, E e8, E e9, E e10) {
     return new ImmutableList<>(new Object[] {
         Objects.requireNonNull(e1),
         Objects.requireNonNull(e2),
         Objects.requireNonNull(e3),
         Objects.requireNonNull(e4),
         Objects.requireNonNull(e5),
         Objects.requireNonNull(e6),
         Objects.requireNonNull(e7),
         Objects.requireNonNull(e8),
         Objects.requireNonNull(e9),
         Objects.requireNonNull(e10)
     });
   }

   @SafeVarargs
   static <E> List<E> varargs(E... elements) {
     int length = elements.length;
     Object[] copy = new Object[length];
     System.arraycopy(elements, 0, copy, 0, length);
     for (Object e : copy) Objects.requireNonNull(e);
     return new ImmutableList<>(copy);
   }

   static final class ImmutableList<E> extends AbstractList<E> {
     final Object[] array;

     ImmutableList(Object[] array) {
       this.array = array;
     }

     @Override
     @SuppressWarnings("unchecked")
     public E get(int index) {
       return (E) array[index];
     }

     @Override
     public int size() {
       return array.length;
     }
   }
}

Reply via email to