В чем смысл перегруженных методов фабрики удобства для коллекций в Java 9

Java 9 поставляется с удобными фабричными методами для создания неизменяемых списков. Наконец, создание списка так же просто, как:

List<String> list = List.of("foo", "bar");

Но существует 12 перегруженных версий этого метода, 11 с 0 - 10 элементами и одна с переменными аргументами.

static <E> List<E>  of(E... elements)

То же самое в случае с Set а также Map,

Поскольку существует метод var args, какой смысл иметь дополнительные 11 методов?

Я думаю, что var-args создают массив, поэтому остальные 11 методов могут пропустить создание дополнительного объекта, и в большинстве случаев это будет делать от 0 до 10 элементов. Есть ли другая причина для этого?

6 ответов

Решение

Из самой документации JEP -

Описание -

Они будут включать в себя перегрузки varargs, поэтому не существует фиксированного ограничения на размер коллекции. Однако созданные таким образом экземпляры коллекции могут быть настроены на меньшие размеры. Будут предоставлены специальные API-интерфейсы (перегрузки с фиксированным аргументом) для десяти элементов. Хотя это вносит некоторую путаницу в API, оно позволяет избежать выделения массивов, инициализации и издержек на сборку мусора, возникающих при вызовах varargs. Важно отметить, что исходный код сайта вызова остается неизменным независимо от того, вызывается ли перегрузка с фиксированным аргументом или varargs.


Редактировать - чтобы добавить мотивацию и как уже упоминалось в комментариях @CKing тоже:

Не Цели -

Не стоит поддерживать высокопроизводительные, масштабируемые коллекции с произвольным количеством элементов. Основное внимание уделяется небольшим коллекциям.

Мотивация -

Создание небольшой неизменяемой коллекции (скажем, набора) включает ее создание, сохранение в локальной переменной, несколько раз вызывая для нее add(), а затем оборачивая ее.

Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));

Java 8 Stream API можно использовать для создания небольших коллекций путем объединения методов и сборщиков потоковой фабрики.

// Java 8
Set<String> set1 = Collections.unmodifiableSet(Stream.of("a", "b", "c").collect(Collectors.toSet()));

Большую выгоду от литералов коллекций можно получить, предоставляя библиотечные API для создания небольших экземпляров коллекций при значительно меньших затратах и ​​рисках по сравнению со сменой языка. Например, код для создания небольшого экземпляра Set может выглядеть так:

// Java 9 
Set set2 = Set.of("a", "b", "c");

Как вы и подозревали, это повышение производительности. Методы Vararg создают массив "под капотом", а наличие метода, который принимает 1-10 аргументов напрямую, позволяет избежать создания этого избыточного массива.

Вы можете найти следующий отрывок из пункта 42 " Эффективной Явы" Джоша Блоха (2-е изд.):

Каждый вызов метода varargs вызывает выделение и инициализацию массива. Если вы эмпирически определили, что не можете позволить себе такую ​​стоимость, но вам нужна гибкость вараггов, существует модель, которая позволяет вам съесть свой торт и съесть его тоже. Предположим, вы определили, что 95 процентов вызовов метода имеют три или менее параметров. Затем объявите пять перегрузок метода, по одному с нулем через три обычных параметра, и один метод varargs для использования, когда число аргументов превышает три [...]

Вы также можете посмотреть на это с другой стороны. Так как методы varargs могут принимать массивы, такой метод будет служить альтернативным средством преобразования массива в List,

String []strArr = new String[]{"1","2"};
List<String> list = List.of(strArr);

Альтернативой этому подходу является использование Arrays.asList но любые изменения, внесенные в List в этом случае будет отражаться в массиве, который не имеет место с List.of, Поэтому вы можете использовать List.of когда ты не хочешь List и массив для синхронизации.

Примечание. Обоснование, данное в спецификации, кажется мне микрооптимизацией. (Это было подтверждено самим владельцем API в комментариях к другому ответу)

Этот шаблон используется для оптимизации методов, которые принимают параметры varargs.

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

public void foo(int num1);
public void foo(int num1, int num2);
public void foo(int num1, int num2, int num3);
public void foo(int... nums);

Это поможет вам избежать создания массива при вызове метода varargs. Шаблон, используемый для оптимизации производительности:

List<String> list = List.of("foo", "bar");
// Delegates call here
static <E> List<E> of(E e1, E e2) { 
    return new ImmutableCollections.List2<>(e1, e2); // Constructor with 2 parameters, varargs avoided!
}

Более интересно то, что, начиная с 3-х параметров, мы снова делегируем конструктору varargs:

static <E> List<E> of(E e1, E e2, E e3) { 
    return new ImmutableCollections.ListN<>(e1, e2, e3); // varargs constructor
}

Пока это кажется странным, но, как я могу догадаться, это зарезервировано для будущих улучшений и, как вариант, потенциальная перегрузка всех конструкторов. List3(3 params), List7(7 params)... и так далее.

Согласно документу Java: коллекции, возвращаемые методами фабрики удобства, занимают больше места, чем их изменяемые эквиваленты.

До Java 9:

Set<String> set = new HashSet<>(3);   // 3 buckets

set.add("Hello");
set.add("World");
set = Collections.unmodifiableSet(set);

В приведенной выше реализации SetСоздается 6 объектов: неизменяемая оболочка; HashSet, который содержит HashMap; таблица ведер (массив); и два экземпляра узла (по одному для каждого элемента). Если виртуальная машина занимает 12 байтов на объект, то 72 байта потребляют как служебные данные, плюс 28*2 = 56 байтов для 2 элементов. Здесь большое количество потребляется накладными расходами по сравнению с данными, хранящимися в коллекции. Но в Java 9 эти издержки очень меньше.

После Java 9:

Set<String> set = Set.of("Hello", "World");

В приведенной выше реализации Setтолько один объект создается, и это займет очень меньше места для хранения данных из-за минимальных издержек.

Другие вопросы по тегам