Подтвердить что ты не робот

Почему у Guava ImmutableList так много перегруженных методов()?

Я просто смотрел на Guava ImmutableList, и я заметил, что метод of() был перегружен 12 раз.

Мне кажется, что все, что им нужно, это:

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

В чем причина стольких подобных изменений?

4b9b3361

Ответ 1

Варгары и дженерики не играют хорошо вместе. Методы Varargs могут вызывать предупреждение с помощью общих аргументов, а перегрузки предотвращают это предупреждение, за исключением редкого случая, когда вы хотите добавить более 11 элементов в неизменяемый список, используя of().

Комментарии в источнике говорят:

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

Обратите внимание, что аннотация Java 7 @SafeVarargs была добавлена ​​специально для устранения необходимости в подобных вещах. Один метод of(E...), аннотированный с помощью @SafeVarargs, может использоваться и не будет давать предупреждения с общими аргументами.

Ответ 2

Там также причина производительности. Каждый вызов метода varargs вызывает распределение и инициализацию массива. Если вы каким-то образом определили, что, например, 95% вызовов имеют 3 или менее аргумента и только 5% с 4 или более, а затем перегрузка, как эта

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 );

приводит к хорошему повышению производительности в 95% случаев. Иными словами, средняя производительность приложения увеличивается.

Ответ 3

В дополнение к другим замечательным ответам здесь есть тонкое преимущество производительности во время выполнения (в дополнение к исключению выделения массива), которое заключается в том, что возвратные реализации zero-arg и single-arg возвращают реализации, которые оптимизированы для представления пустых и одиночных -инструменты (соответственно).

Если бы у нас не было отдельных перегрузок методов для них и включался только один метод на основе varargs, тогда этот метод выглядел бы примерно так:

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);
    }
}

Производительность коммутатора (или проверки if-else) не будет плохой для большинства вызовов, но она по-прежнему не нужна, поскольку может просто перегружать метод для каждой оптимизации, а компилятор всегда знает, какую перегрузку нужно вызывать. На клиентский код нет бремени, так что это легко победить.