Почему 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
.