Почему EnumSet имеет много перегруженных методов" of"?


Проходя через EnumSet<E> of метод, я видел несколько перегруженных реализаций метода of:

public static <E extends Enum<E>> EnumSet<E> of(E e)

public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)

.
.

public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)

, а затем еще один перегруженный метод с varargs

public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
    EnumSet<E> result = noneOf(first.getDeclaringClass());
    result.add(first);
    for (E e : rest)
        result.add(e);
    return result;
}

Когда этот varargs мог бы обрабатывать другие реализации, почему этот метод перегружен таким образом? Есть ли для этого какая-то конкретная причина?

Я прошел через Джавадок того же самого, но не смог найти никакого убедительного объяснения.
4 33

4 ответа:

Методы Varargs создают массив.

public static void foo(Object... args) {
  System.out.println(args.length);
}

Это работает из-за неявного создания массива. EnumSet - это класс, предназначенный для очень, очень быстрой работы, поэтому, создавая все дополнительные перегрузки, они могутпропустить шаг создания массива в первых нескольких случаях. Это особенно верно, поскольку во многих случаях Enum не имеет такого количества элементов, и если они есть, то EnumSet может содержать не все из них.

Джавадок для EnumSet<E> of(E e1, E e2, E e3, E e4, E e5):

Создает набор перечислений изначально содержащий указанные элементы. Перегрузки этого метода существуют для инициализации набора перечислений с одним до пяти элементов. Предусмотрена шестая перегрузка, которая использует функцию varargs. Эта перегрузка может быть использована для создания набора перечислений, изначально содержащего произвольное число элементов, но, скорее всего, будет выполняться медленнее, чем перегрузки, которые не используют varargs.

Varags создает массив, то есть когда мы вызываем

void x(int...x) {...}
..
x(1);

Компилятор заменяет последнюю строку следующим образом:

x(new int[] {1});

Этого не произойдет, если у нас есть перегруженный метод с 1 arg:

void x(int...x) {...}
void x(int x) {...}

Тогда компилятор выберет второй метод.

Потому что этот класс был разработан Джошем Блохом, и этот парень знает, как все работает. :) Помимо создания массива, метод varargs содержит цикл, который больше подходит для JIT для оптимизации кода.

Например, если мы рассмотрим реализацию перегруженной версии с пятью параметрами:

result.add(e1);
result.add(e2);
result.add(e3);
result.add(e4);
result.add(e5);

Мы замечаем, что это своего рода уже развернутый цикл , который может выглядеть следующим образом:

for (E e : Arrays.asList(e1, e2, e3, e4, e5)) {
   result.add(e);
}

Кроме того, более короткие и простые методы, скорее всего, будут инлайнированные, чем более длинные и сложные.

Из javadoc:

Перегрузки этого метода существуют для инициализации набора перечислений с помощью от одного до пяти элементов. Шестая перегрузка обеспечивается тем, что использует функцию varargs. Эта перегрузка может быть использована для создания множество перечислений, изначально содержащее произвольное число элементов, но скорее всего, он будет работать медленнее, чем перегрузки, которые не используют варарги.