让我们从一个例子开始。首先是我认为显而易见的:
List<String> wordList = Arrays.asList("just", "a", "test");
Set<String> wordSet = new HashSet<>(wordList);
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);
输出结果将显示不同的“顺序”-因为我们通过1-100
的添加使容量更大,并且条目已移动。它们仍然是3个-但是以不同的顺序(如果可以称之为顺序)。
因此,是的,在流操作之间修改Set
后,“顺序”可能会改变。
由于您说在创建后Set
不会被修改-因此在当前实现下,顺序被保留在此刻(无论是什么)。更确切地说,一旦条目被放入Set
中,它就不会在内部随机化。
但是绝对不要依赖这一点-任何时候都不要这样做。事情可能会毫无预兆地改变,因为合同允许这样做-文件不对任何顺序做出任何保证- Set
主要用于唯一性。
例如,jdk-9不可变的Set
和Map
确实具有内部随机化,并且“顺序”将从运行到运行改变:
Set<String> set = Set.of("just", "a", "test");
System.out.println(set);
这是允许打印的:
[a, test, just] or [a, just, test]
编辑
这是随机化模式的样子:
/**
* A "salt" value used for randomizing iteration order. This is initialized once
* and stays constant for the lifetime of the JVM. It need not be truly random, but
* it needs to vary sufficiently from one run to the next so that iteration order
* will vary between JVM runs.
*/
static final int SALT;
static {
long nt = System.nanoTime();
SALT = (int)((nt >>> 32) ^ nt);
}
这个函数的作用:
先取一个长整型数,将其前32位与后32位进行异或运算,然后从结果中取最后32位(通过强制类型转换为整型)。使用异或的原因是因为它具有50%的0和1分布,所以不会改变结果。
如何在代码中使用(例如对于一个包含两个元素的Set
):
// based on SALT set the elements in a particular iteration "order"
if (SALT >= 0) {
this.e0 = e0;
this.e1 = e1;
} else {
this.e0 = e1;
this.e1 = e0;
根据这里的信息,我猜jdk9中内部随机排列的实现方式是这样的:
最终的安全特性是不可变Set元素和Map键的随机迭代顺序。HashSet和HashMap的迭代顺序一直未指定,但相当稳定,导致代码对该顺序有意外的依赖。当迭代顺序发生变化时,这会导致代码出现问题,这种情况偶尔会发生。新的Set / Map集合在运行时更改它们的迭代顺序,希望能够在测试或开发中尽早排除顺序依赖。
因此,这基本上是为了打破所有依赖于Set/Map顺序的代码。当人们从java-7升级到java-8并且依赖于HashMap的顺序(LinkedNode)时,也发生了同样的事情,由于TreeNode的引入,顺序变得不同了。如果保留这样的功能并且人们依赖它多年,那么很难删除它并执行一些优化,例如HashMap移动到TreeNode;因为现在你被迫保持那个顺序,即使你不想要。但这只是我的猜测,请将其视为猜测。
ImmutableCollections.SetN
- 如何选择数组中的插槽... - Eugeneskip
和 limit
也不再受迭代顺序的限制。 - HolgerHashSet
的迭代顺序保持不变--没有这样的保证。Stream
实现,如果Spliterator
没有报告ORDERED
特征,则不需要维护迭代顺序。skip(1)
不需要跳过第一个元素,因为没有“第一个”元素,而只需跳过一个元素。Stream
实现将像limit(size-n)
一样对待无序但SIZED
源的skip(n)
,因为这也会有效地跳过n个元素,且工作量更少。HashSet
的迭代顺序没有改变,这也会破坏您的批处理场景。
List
,那就是它们的设计初衷。 - fps