如果元素是重复的,为什么Set.of()会抛出IllegalArgumentException?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如果元素是重复的,为什么Set.of()会抛出IllegalArgumentException?相关的知识,希望对你有一定的参考价值。

在Java 9中,在Set接口上引入了新的静态工厂方法,称为of(),它接受多个元素,甚至是一个元素数组。

我想将一个列表转换为一个集合,以删除集合中的任何重复条目,这可以通过以下方式完成(在Java 9之前):

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

但是我认为使用这个新的Java 9静态工厂方法会很酷:

Set.of(list.toArray())

其中list是之前定义的字符串列表。

但是,当元素重复时,java抛出了IllegalArgumentException,也在方法的Javadoc中说明了。为什么是这样?

编辑:这个问题不是关于概念上等效主题的另一个问题的重复,Map.of()方法,但明显不同。并非所有()方法的静态工厂都表现相同。换句话说,当我问一些关于Set.of()方法的东西时,我不会点击处理Map.of()方法的问题。

答案

Set.of()工厂方法为给定数量的元素生成不可变的Sets。

在支持固定数量的参数(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之前就已经可用了。

另一答案

Set.of()是手动创建小型q​​azxswpoi的简短方法。在这种情况下,如果你给它重复值,那将是一个明显的编程错误,因为你应该自己写出这些元素。即Set显然是程序员的错误。

你很酷的方式实际上是一个非常糟糕的方式。如果你想将Set.of("foo", "bar", "baz", "foo");转换为List,你可以用Set或你想要的任何其他方式(例如使用溪流和收集Set<Foo> foo = new HashSet<>(myList);)。优点包括不做无用的toSet(),你自己的toArray()的选择(你可能想要一个Set来保持秩序)等。缺点包括必须输入更多的代码字符。

LinkedHashSetSet.of()List.of()方法背后的原始设计理念(以及它们的众多重载)在这里解释了Map.of()What is the point of overloaded Convenience Factory Methods for Collections in Java 9,其中提到了重点是小型集合,这是内部API的常见问题,因此性能优势可以有。虽然目前这些方法委托varargs方法没有提供任何性能优势,但这很容易改变(虽然不确定阻止是什么)。

另一答案

你希望这是一个“最后的胜利”,就像here我想的那样,但这是一个刻意的决定(正如斯图尔特马克斯 - 这些解释的创造者)。他甚至有一个这样的例子:

HashSet

选择是因为这可能容易出错,所以应该禁止它。

另请注意Map.ofEntries( "!", "Eclamation" .... // lots of other entries "" "|", "VERTICAL_BAR" ); 返回一个不可变的Set.of(),所以你可以将你的Set包装成:

Set
另一答案

Collections.unmodifiableCollection(new HashSet<>(list)) List.ofSet.ofMap.of静态工厂方法的主要设计目标是使程序员能够通过在源代码中明确列出元素来创建这些集合。当然,存在对少量元素或条目的偏见,因为它们更常见,但这里的相关特征是元素在源代码中列出。

如果向Map.ofEntries提供重复元素或提供给Set.ofMap.of的重复键,行为应该是什么?假设元素在源代码中明确列出,这可能是编程错误。诸如首胜或最后胜利之类的替代方案似乎可能会默默地掩盖错误,因此我们认为将重复作为错误是最好的行动方案。如果明确列出了元素,那么如果这是一个编译时错误就会很好。但是,直到运行时才检测到重复项的检测,因此在那时抛出异常是我们能做的最好的事情。

*将来,如果所有参数都是常量表达式或者是常量可折叠的,那么Set或Map创建也可以在编译时进行评估,也可以进行常量折叠。这可能会在编译时检测到重复项。

如果您有一组元素并且想要对它们进行重复数据删除的用例呢?这是一个不同的用例,Map.ofEntriesSet.of处理得不好。你必须先创建一个中间数组,这非常麻烦:

Map.ofEntries

这不编译,因为Set<String> set = Set.of(list.toArray()); 返回list.toArray()。这将产生一个Object[],不能分配给Set<Object>。你想要Set<String>给你一个toArray代替:

String[]

这个类型检查,但它仍然会引发重复的异常!提出了另一种选择:

Set<String> set = Set.of(list.toArray(new String[0]));

这是有效的,但是你得到了一个Set<String> set = new HashSet<>(list); ,它是可变的,占用了比HashSet返回的集合更多的空间。您可以通过Set.of对元素进行重复数据删除,从中获取数组,然后将其传递给HashSet。这会起作用,但是很可怕。

幸运的是,这已在Java 10中修复。您现在可以编写:

Set.of

这会从源集合的元素创建一个不可修改的集合,并且重复项不会引发异常。相反,使用任意一个副本。有类似的方法Set<String> set = Set.copyOf(list); List.copyOf。作为奖励,如果源集合已经是正确类型的不可修改集合,则这些方法会跳过创建副本。

另一答案

Map.copyOf

结果集的元素类型将是数组的组件类型,并且集的大小将等于数组的长度。

抛出:

Set.of​(E... elements)

很明显,这不会进行任何重复测试,因为IllegalArgumentException - if there are any duplicate elements 的大小将是数组的长度。

这个方法就是能够在一行中获得填充的Set

Set

但你必须小心自己的副本。 (这将简单地迭代varargs并将它们添加到新的Set.of("A","B","C"); 中。

以上是关于如果元素是重复的,为什么Set.of()会抛出IllegalArgumentException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 XCode 会抛出重复的符号错误?

为啥这段代码会抛出 java.lang.***Error [重复]

抛出啥异常? (Python)[重复]

为啥非空列表会抛出空指针异常?

如果我不告诉 C++ 中要抛出啥类型的对象,会抛出啥 throw 语句?

假设x是含有5个元素的列表,那么切片操作x[10:]是无法执行的,会抛出异常?