41

I was just looking at Guava's ImmutableList and I noticed that the of() method was overloaded 12 times.

It looks to me that all they needed was:

static <E> ImmutableList<E> of();
static <E> ImmutableList<E> of(E element); // not even necessary
static <E> ImmutableList<E> of(E... elements);

What's the reason for having so many similar variations?

jjnguy
  • 136,852
  • 53
  • 295
  • 323
  • 6
    And they all pass their parameters to an internal varargs method anyway...huh. I'm going to have to raise an eyebrow at this one. Hmm, the source has a comment "These go up to eleven. After that, you just get the varargs form, and whatever warnings might come along with it. :(". I'm not sure what warnings it's referring to, though. – Tim Stone Sep 17 '10 at 18:22
  • @Tim, this would probably make a good answer, worth at least and upvote, and probably the accepted answer. – jjnguy Sep 17 '10 at 18:34
  • 8
    +1 for Google for going up to eleven! – romacafe Sep 17 '10 at 18:54
  • @Justin Thanks, I do need to get out of the habit of commenting verbosely instead of answering and then adding additional information, heh. At any rate, I think [ColinD has it covered](http://stackoverflow.com/questions/3737882/3737976#3737976) now, so I'll leave it at that. – Tim Stone Sep 17 '10 at 18:55
  • @Tim, I would recommend editing his answer to add the comment from the source. I think that adds a lot to the answer. I'd do it, but I don't have the source on hand. – jjnguy Sep 17 '10 at 19:02
  • @jjnguy: I've added the source comment now. I checked the source before posting to see what they had to say, but didn't think it really added much information. – ColinD Sep 17 '10 at 19:12
  • @Colin, without the source comments, it is impossible to be sure that your answer was correct. This serves as 'proof' – jjnguy Sep 17 '10 at 19:17
  • 3
    @romacafe I think Google got beat: http://svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/codehaus/groovy/runtime/ArrayUtil.java Though Groovy does this for performance reasons. It was recently discussed on Hack News ( http://news.ycombinator.com/item?id=1951803 ) – Etienne Neveu Dec 04 '10 at 12:32
  • 1
    "These go to eleven." -- http://en.wikipedia.org/wiki/Up_to_eleven – kevinarpe Mar 14 '13 at 04:51

3 Answers3

42

Varargs and generics do not play nicely together. Varargs methods can cause a warning with generic arguments, and the overloads prevent that warning except in the rare case that you want to add more than 11 items to the immutable list using of().

The comments in the source say:

These go up to eleven. After that, you just get the varargs form, and whatever warnings might come along with it. :(

Note that Java 7's @SafeVarargs annotation was added specifically to eliminate the need for this sort of thing. A single of(E...) method annotated with @SafeVarargs could be used and would not give warnings with generic arguments.

ColinD
  • 108,630
  • 30
  • 201
  • 202
  • Hey, Could you potentially update your answer to reflect the performance part that is referenced by @Rinke? I think that it is worth mentioning. – João Rebelo Aug 25 '16 at 22:36
  • 3
    @JoãoRebelo: That's not actually true though... the methods just immediately go and call a varargs method themselves. – ColinD Aug 26 '16 at 17:31
  • Do you mean in this specific case? Or I am misunderstanding what you meant? – João Rebelo Aug 28 '16 at 21:41
  • 3
    @JoãoRebelo: I mean that for the majority of the `ImmutableList.of(...)` overloads, they are implemented as `construct(e1, e2, e3, e4)` etc. where `construct` is defined as `construct(Object... elements)`. So an array is allocated anyway. (I guess there are some advantages, like knowing that the array passed to `construct` is an internal array that can't be modified by the caller, so you end up not needing to copy that array in most cases, but I don't think that's especially worth focusing on--it isn't the main reason the methods were created certainly.) – ColinD Aug 29 '16 at 15:45
  • That is interesting. I just went through the source to check that curiosity. This is a nice piece of info and some reminder as to not always work on assumptions. – João Rebelo Aug 30 '16 at 10:43
  • @ColinD: Good to know. I assumed and didn't check the source code. Thanks for the info. – Rinke Sep 01 '16 at 10:12
  • Why 11? is it a limit that they thought not many would encounter? or does it have some magic? – asgs Oct 13 '16 at 20:13
15

There's also a performance reason. Every invocation of a varargs method causes an array allocation and initialization. If you have somehow determined that e.g. 95% of the calls are with 3 or less arguments and only 5% with 4 or more, then overloading like this

public static <E> ImmutableList<E> of();
public static <E> ImmutableList<E> of( E e );
public static <E> ImmutableList<E> of( E e1, E e2 );
public static <E> ImmutableList<E> of( E e1, E e2, E e3 );
public static <E> ImmutableList<E> of( E e1, E e2, E e3, E... es );

leads to a nice performance boost in 95% of the cases. Differently put, the average case performance goes up.

Rinke
  • 6,095
  • 4
  • 38
  • 55
  • 9
    Note: Although the principle holds, I just learned from ColinD that this actually isn't true for Guava because the overloaded methods result in a varargs call anyway (in the current implementation). – Rinke Sep 01 '16 at 10:14
4

In addition to the other great answers here, there's a subtle runtime performance advantage (in addition to avoiding the array allocation), which is that the zero-arg and single-arg overloads return implementations that are optimized for representing empty and single-instances lists (respectively).

If we didn't have separate method overloads for these and only included a single varargs-based method, then that method would look something like this:

public static <E> ImmutableList<E> of(E... es) {
    switch (es.length) {
      case 0:
        return emptyImmutableList();
      case 1:
        return singletonImmutableList(es[0]);
      default:
        return defaultImmutableList(es);
    }
}

The performance of the switch case (or if-else checks) wouldn't be bad for most calls, but it's still unnecessary since can just have method overloads for each optimization, and the compiler always knows which overload to call. There's no burden placed on the client code, so it's an easy win.

Andrew McNamee
  • 1,705
  • 1
  • 13
  • 11