据我所知,Set in java是一个无序集合,迭代器将以其选择的某个顺序处理项目(我可能在这里错了)但确保它处理集合中的所有元素。
在Java8中,集合中的stream()API已经引入了跳过和限制功能。所以我想知道从流处理的项目的顺序是否保持相同,无论我开始流的次数或每次都是随机的?如果在流之间修改集合,订单会改变吗?
可能无关紧要,但我在这里提出问题: 现在遇到这个问题,我有一套2000或者其他什么东西在创建后不会被修改,我正在进行50次批处理操作,涉及每个批次的网络调用。我有一个启动参数,每次调用后增加50。如果我在我的Set上使用一个带有“start”的流作为每个批处理的skip参数,那么它对于每个批处理都是一个新流吗?因此,流程的顺序保持不变。显然,我不会多次相同的条目,更重要的是我不会错过任何条目。最简单的事情是对我来说是一个Arraylist,但我想知道我是否真的需要创建一个集合。
答案 0 :(得分:9)
让我们从这里开始一个例子。首先,我认为这是显而易见的:
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);
输出将显示不同的&#34;顺序&#34; - 因为我们已经增加了容量(通过1-100
添加)并且条目已经移动。他们仍然在那里 - 但是以不同的顺序(如果可以称为顺序)。
所以,是的,一旦你在流操作之间修改了Set
,那么&#34;命令&#34;可能会改变。
由于您说创建后Set
不会被修改 - 目前在当前实现(无论是什么)下,订单仍保留。或者更准确地说,它不是内部随机化的 - 一旦条目被放入Set
。
但这绝对不是一个人依赖的东西。事情可能会发生变化,恕不另行通知,因为合同允许这样做 - 文档不会对任何订单做出任何保证 - 毕竟Set
是关于唯一性的。
举个例子,jdk-9不可变Set
和Map
确实有内部随机化和&#34;顺序&#34;将从一次运行变为运行:
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位(通过强制转换为int)。使用XOR是因为它具有50%的零和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内部随机化部分的猜测,最初取自here,相关部分:
最终的安全功能是不可变Set元素和Map键的随机迭代顺序。 HashSet和HashMap迭代顺序一直未指定,但相当稳定,导致代码无意中依赖于该顺序。当迭代顺序发生变化时,这会导致事情中断,这种情况偶尔会发生。新的Set / Map集合将其运行的迭代顺序更改为运行,希望在测试或开发早期清除顺序依赖性
因此,基本上打破所有依赖于Set
/ Map
订单的代码。当人们从java-7迁移到java-8并且依赖于HashMap的顺序(LinkedNode
s)时,同样的事情发生了,由于TreeNode
的引入,这是不同的。如果你留下这样的功能并且人们依赖它多年 - 很难删除它并执行一些优化 - 比如HashMap移动到TreeNode
s;因为现在你被迫保留这个命令,即使你不想这样做。 但这显然只是猜测,请将其视为
答案 1 :(得分:5)
这里有两个方面。作为Eugene correctly pointed out,您不能假设HashSet
的迭代顺序保持不变 - 没有这样的保证。
但另一方面是Stream
实现,当Spliterator
未报告ORDERED
特征时,不需要维护迭代顺序。
换句话说,如果流是无序的,则skip(1)
不需要跳过第一个元素,因为没有“first”元素,只是跳过一个元素。
虽然流不太可能实现随机化,但它们试图利用特征来最小化工作。一个看似合理的情况是Stream
实施将skip(n)
处理无序但SIZED
来源,就像limit(size-n)
一样,因为它也会有效地跳过 n 元素,工作量少。
今天可能不会发生这样的优化,但在下一个版本中,即使在HashSet
的迭代顺序没有改变的情况下,也会破坏批处理场景。