不可变集合和映射上的 JDK9 随机化
阅读这个问题和Eugene给出的答案,我发现JDK9不可变集合和映射将引入一个随机性的来源,这将影响它们的遍历。这意味着迭代顺序确实是随机的,至少在JVM的不同运行之间是这样。
由于规范不保证集合和映射的任何遍历/迭代顺序,因此这绝对没问题。事实上,代码绝不能依赖于特定于实现的细节,而必须依赖于规范。
我知道今天,使用JDK 8,如果我有,即a并执行此操作(取自链接的答案):HashSet
Set<String> wordSet = new HashSet<>(Arrays.asList("just", "a", "test"));
System.out.println(wordSet);
for (int i = 0; i < 100; i++) {
wordSet.add("" + i);
}
for (int i = 0; i < 100; i++) {
wordSet.remove("" + i);
}
System.out.println(wordSet);
然后,元素的迭代顺序将发生变化,并且两个输出将不同。这是因为在集合中添加和删除 100 个元素会更改和重构元素的内部容量。这是完全有效的行为。我不是在这里问这个。HashSet
但是,对于JDK9,如果我这样做:
Set<String> set = Set.of("just", "a", "test");
System.out.println(set);
然后,在JVM的另一个实例中,我运行相同的代码,输出可能不同,因为引入了随机化。
到目前为止,我已经在youtube上找到了这个优秀的视频(第44:55分钟),其中Stuart Marks说这种随机化的一个动机是:
(...)人们编写的应用程序无意中依赖于迭代顺序。(...)所以,无论如何,迭代顺序是一个大问题,我认为有很多代码对迭代顺序有潜在的依赖性,但尚未被发现。(...)因此,我们对此的回应是故意随机化新集合中
Set
和Map
中的迭代顺序。因此,虽然在集合的迭代顺序不可预测的但稳定之前,这些是可预测的不可预测的。因此,每次JVM启动时,我们都会得到一个随机数,并将其用作与哈希值混合的种子值。因此,如果您运行一个初始化集合的程序,然后以任何顺序打印出元素,您将获得一个答案,然后,如果您再次调用JVM并运行相同的程序,则元素集通常会以不同的顺序出现。所以,这里的想法是(...)如果你的代码中存在迭代顺序依赖关系,过去发生的事情是一个新的JDK版本出来了,你测试你的代码,(...)它需要几个小时的调试才能追踪到迭代顺序的某种变化。这意味着该代码中存在一个依赖于迭代顺序的错误。现在,如果你更频繁地改变迭代顺序,就像每次JVM调用一样,那么(我们希望)奇怪的行为会更频繁地表现出来,事实上,我们希望在你进行测试的时候......
因此,动机是明确的,也很明显,这种随机化只会影响新的不可变集合和映射。
我的问题是:这种随机化还有其他动机吗?它有什么好处?