2

I find it strange that in the List interface are defined 10 methods of(...)

 static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)

instead of just

    @SuppressWarnings("varargs")
    static <E> List<E> of(E... elements){...}

Actually, if I look at the implementation of the varargs version it's even more strange...(a switch on 0,1,2, n elements...)

Any reason for that?

Glasnhost
  • 223
  • 1
  • 6
  • 2
    Is the source you're looking at online somewhere you could link to? At a guess, I would say **performance**, but the actual implementation would probably give something more specific to say. – IMSoP Jan 26 '21 at 18:19
  • My guess is that the interface was written before Java allowed varargs. – user949300 Jan 26 '21 at 18:21
  • 2
    @user949300 your guess is wrong, this method was added long after varargs – gnat Jan 26 '21 at 18:26

1 Answers1

6

With absolutely no prior knowledge of the specific case, I would guess: performance.

Given a function with a fixed set of parameters, a compiler can optimise how those parameters are laid out in memory, and even optimise the entire function to a fixed set of processor instructions.

A generalised implementation instead needs machinery for detecting the number of parameters actually passed, collecting them into some intermediate representation, and iterating over them.

Consider an example of sum, in an imaginary language, implemented variadically:

function sum(int[] ...args) {
    local accumulator = 0;
    foreach ( args as next_arg ) {
        accumulator += next_arg;
    }
    return accumulator;
}

Even without knowing the low-level mechanics of passing and allocating variables, we can see that there's a lot going on here. Now compare to implementations with fixed argument lists:

function sum(int a) { return a; }
function sum(int a, int b) { return a + b; }
function sum(int a, int b, int c) { return a + b + c; }
function sum(int a, int b, int c, int d) { return a + b + c + d; }

Here, no intermediate storage needs to be allocated for args and accumulator, and the implementations are easy to map to low-level instructions.

While it's possible for a sufficiently powerful compiler to generate the optimised cases from the generalised one, it's far from easy. So where performance matters more than human convenience, it can be worth hand-crafting at least some of the cases.

IMSoP
  • 5,722
  • 1
  • 21
  • 26