过去,我曾说过要安全地复制集合,请执行以下操作:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
要么
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
但是,这些“复制”构造函数,类似的静态创建方法和流是否真的安全?在哪里指定规则?所谓安全,是指Java语言和针对恶意调用者强制执行的集合提供的基本语义完整性保证,并假设有合理的支持SecurityManager
且没有缺陷。
我很高兴与方法投掷ConcurrentModificationException
,NullPointerException
,IllegalArgumentException
,ClassCastException
,等,或者甚至挂起。
我选择String
了一个不可变类型参数的示例。对于这个问题,我对具有自己陷阱的可变类型集合的深层副本不感兴趣。
NavigableSet
其他Comparable
基于集合的集合有时可以检测到类是否未compareTo()
正确实现并引发异常。不清楚不可信参数的含义。您的意思是说邪恶的人会制作坏字符串的集合,当您将它们复制到集合中时,会发生不好的事情吗?不,集合框架非常可靠,从1.2开始就存在。
HashSet
(以及所有其他散列集合)通常依赖于hashCode
元素实现的正确性/完整性,TreeSet
并且PriorityQueue
依赖于Comparator
(而且您甚至无法创建一个等效副本而不接受自定义比较器(如果存在),则EnumSet
信任特定enum
类型的完整性,该类型在编译后从未进行过验证,因此,不是由生成javac
或手工生成的类文件可以颠覆它。
new TreeSet<>(strs)
哪里strs
是NavigableSet
。这不是批量复制,因为结果TreeSet
将使用源的比较器,这甚至对于保留语义是必要的。如果只处理包含的元素toArray()
就可以了,那就去吧;它甚至可以保持迭代顺序。如果您对“采用元素,验证元素,使用元素”没问题,那么您甚至无需复制。当您要验证所有元素,然后使用所有元素时,问题就开始了。然后,您将无法信任TreeSet
带有自定义比较器的副本
checkcast
对于每个元素,唯一具有a效果的批量复制操作具有toArray
特定类型。我们总是以它结束。通用集合甚至都不知道其实际元素类型,因此其副本构造函数无法提供类似的功能。当然,您可以将任何支票推迟到正确的使用前,但是,我不知道您的问题是针对什么。当您可以在使用元素之前进行检查并立即失败时,就不需要“语义完整性”。