Почему Set.of() выбрасывает исключение IllegalArgumentException, если элементы являются дублирующими?

В Java 9 были введены новые статические методы factory на интерфейсе Set, вызываемые(), которые принимают несколько элементов или даже массив элементов.

Я хотел превратить список в набор, чтобы удалить любые повторяющиеся записи в наборе, которые можно сделать (до Java 9), используя:

Set<String> set = new HashSet<>();
set.addAll(list);

Но я подумал, что было бы здорово использовать этот новый статический factory метод Java 9: ​​

Set.of(list.toArray())

где list - это список строк, ранее определенный.

Но, увы, java выбрал IllegalArgumentException, когда элементы были дубликатами, также указанными в Javadoc метода. Почему это?

Изменить: этот вопрос не является дубликатом другого вопроса о концептуально эквивалентной теме - методе Map.of(), но совершенно другой. Не все статические методы factory of() ведут себя одинаково. Другими словами, когда я задаю вопрос о методе Set.of(), я бы не стал нажимать на вопрос, связанный с методом Map.of().

Ответы

Ответ 1

Методы Set.of() factory создают неизменяемый Set для заданного количества элементов.

В вариантах, поддерживающих фиксированное количество аргументов (static <E> Set<E> of​(), static <E> Set<E> of​(E e1), static <E> Set<E> of​(E e1,E e2) и т.д.), требование не иметь дубликатов легче понять - когда вы вызываете метод Set.of(a,b,c), вы заявляете, что хотите создать неизменяемый Set из точно 3 элементов, поэтому, если аргументы содержат дубликаты, имеет смысл отклонить ваш ввод вместо создания меньшего Set.

В то время как вариант Set<E> of​(E... elements) отличается (если позволяет создать Set произвольного количества элементов), он следует той же логике других вариантов. Если вы передадите этот метод n, вы заявляете, что хотите создать неизменяемый Set элементов точно n, поэтому дубликаты не разрешены.

Вы все равно можете создать Set из List (с потенциальными дубликатами) в одном слоте, используя:

Set<String> set = new HashSet<>(list);

который уже был доступен до Java 9.

Ответ 2

Set.of() - это короткий способ создания вручную небольшого Set. В этом случае это была бы вопиющая ошибка программирования, если бы вы дали ей повторяющиеся значения, так как вы должны сами выписывать элементы. То есть Set.of("foo", "bar", "baz", "foo"); явно является ошибкой в ​​части программиста.

Ваш классный способ на самом деле очень плохой способ. Если вы хотите преобразовать List в Set, вы можете сделать это с помощью Set<Foo> foo = new HashSet<>(myList); или любым другим способом (например, с потоками и сбором toSet()). Преимущества включают в себя не использование бесполезного toArray(), выбор вашего собственного Set (вы можете захотеть LinkedHashSet сохранить порядок) и т.д. Недостатки включают в себя набрать еще несколько символов кода.

Исходная идея дизайна методов Set.of(), List.of() и Map.of() (и их многочисленные перегрузки) объясняется здесь В чем смысл перегрузки Удобство Factory Методы коллекций в Java 9 и здесь, где упоминалось, что Основное внимание уделяется небольшим коллекциям, что является чем-то очень распространенным во всем внутреннем интерфейсе API, поэтому преимущества производительности могут быть достигнуты. Хотя в настоящее время методы делегируют метод varargs, не предлагая преимущества производительности, это можно легко изменить (не уверен, что это задержка на этом).

Ответ 3

Вы ожидаете, что это будет "последний выигрыш", как, например, HashSet, но это было преднамеренное решение (как это объясняет Стюарт Маркс). У него даже есть пример:

Map.ofEntries(
   "!", "Eclamation"
   .... // lots of other entries
   ""
   "|", "VERTICAL_BAR"
);

Выбор состоит в том, что, поскольку это может быть подвержено ошибкам, они должны запрещать его.

Также обратите внимание, что Set.of() возвращает неизменяемый Set, поэтому вы можете обернуть Set в:

Collections.unmodifiableCollection(new HashSet<>(list))

Ответ 4

Set.of(E... элементы)

Тип элемента результирующего набора будет типом компонента массива, а размер набора будет равен длине массива.

Броски:

IllegalArgumentException - if there are any duplicate elements

Это ясно, что это не делает повторного теста, так как размер Set будет длиной массива.

Метод только для того, чтобы получить заполненный Set в одной строке

Set.of("A","B","C");

Но вы должны быть осторожны с дубликатом. (Это просто повторит varargs и добавит их в новый Set.