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

17

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

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 элементов. Есть ли другая причина для этого?

    
задан ares 29.01.2017 в 09:20
источник

5 ответов

23

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

Описание -

  

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

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

Non-Goals -

  

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

Мотивация -

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

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

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

// 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");
    
ответ дан nullpointer 29.01.2017 в 09:27
источник
8

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

    
ответ дан Mureinik 29.01.2017 в 09:25
источник
6

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

  

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

    
ответ дан Jubobs 29.01.2017 в 16:25
источник
3

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

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

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

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

    
ответ дан CKing 29.01.2017 в 09:32
источник
-1

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

До Java 9: ​​

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

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

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

После Java 9: ​​

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

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

    
ответ дан Mohit Tyagi 03.10.2017 в 09:41
источник