Какой смысл перегружать Удобство Factory Методы коллекций в Java 9

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

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

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

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

То же самое происходит с Set и Map.

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

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

Ответы

Ответ 1

Из самого документа JEP -

Описание -

Они будут включать перегрузки varargs, так что нет фиксированного предела по размеру коллекции. Однако созданные экземпляры коллекции могут быть настроены для меньших размеров. Специальные API-интерфейсы (фиксированный аргумент перегрузки) для до десяти элементов. Пока это вводит некоторый беспорядок в API, он избегает выделения массива, инициализации и накладных расходов на сбор мусора, которые понесены varargs calls.. Значительно, исходный код сайта вызова одинаковый независимо от того, вызвана ли перегрузка с фиксированным-arg или varargs.


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

Non-Goals -

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

Мотивация -

Создание небольшой немодифицируемой коллекции (скажем, набора) предполагает ее создание, сохранение ее в локальной переменной и несколько раз добавление() к ней, а затем ее завершение.

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

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

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

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

По Java doc: коллекции, возвращаемые удобными методами factory, более эффективны по площади, чем их изменяемые эквиваленты.

До Java 9: ​​

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

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

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

После Java 9: ​​

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

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