据我所知,Set in java是一个无序集合,迭代器将按照其选择的某个顺序处理项目(我可能在这里错了),但确保它处理集合中的所有元素.
在Java8中,集合中的stream()API已经引入了跳过和限制功能.所以我想知道从流处理的项目的顺序是否保持相同,无论我开始流的次数或每次都是随机的?如果在流之间修改集合,订单会改变吗?
可能是无关紧要但我在这里提出问题:现在遇到问题,我有一套2000或者其他什么东西在创建后不会被修改,我正在进行50个批量操作,涉及每个批次的网络调用.我有一个启动参数,每次调用后增加50.如果我在我的Set上使用一个带有"start"的流作为每个批处理的skip参数,那么它对于每个批处理都是一个新流吗?因此,流程的顺序保持不变.显然,我不会多次相同的条目,更重要的是我不会错过任何条目.最简单的事情对我来说是一个Arraylist但我想知道我是否真的需要创建一个集合.
1> Eugene..:
让我们从这里的例子开始吧.首先,我认为这是显而易见的:
List wordList = Arrays.asList("just", "a", "test");
Set 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
添加)并且条目已经移动.他们仍然在那里 - 但是以不同的顺序(如果可以称为顺序).
所以,是的,一旦你修改Set
了流之间的操作,"订单"可能会改变.
既然你说post创建Set
将不会被修改 - 在当前的实现(无论是什么)下,订单目前保留.或者更确切地说,它不是内部随机化的 - 一旦条目被放入Set
.
但这绝对不是一个人依赖的东西.事情可能会在没有通知的情况下发生变化,因为合同被允许这样做 - 文档并没有对任何订单做出任何保证 - Set
毕竟这是关于唯一性的.
举个例子来说明jdk-9 Immutable Set
并且Map
有一个内部随机化,"order"将从run到run改变:
Set 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内部随机化部分的猜测,最初取自这里,相关部分:
最后的安全功能是不可变Set元素和Map键的随机迭代顺序.HashSet和HashMap迭代顺序一直未指定,但相当稳定,导致代码无意中依赖于该顺序.当迭代顺序发生变化时,这会导致事情中断,这种情况偶尔会发生.新的Set/Map集合将其运行的迭代顺序更改为运行,希望在测试或开发早期清除顺序依赖性
所以它基本上是打破所有依赖于Set
/的订单的代码Map
.当人们从java-7迁移到java-8并且依赖于HashMap的顺序时,同样的事情发生了LinkedNode
,由于TreeNode
s的引入,这是不同的.如果你留下这样的功能并且人们依赖它多年 - 很难将它删除并执行一些优化 - 比如HashMap转移到TreeNode
s; 因为现在你被迫保留这个命令,即使你不想这样做.但这显然只是猜测,请这样对待它
@Eugene你对随机化动机的猜测是正确的.我记得当时在core-libs dev邮件列表上阅读它.即使是OpenJDK测试代码也有很多这些依赖项,而且要解决这个问题还有一些工作要做.他们想避免再次落入同一个陷阱.
关于迭代顺序的很好的答案,但是一旦流是无序的,强调流上的`skip`和`limit`无论如何都没有绑定到迭代顺序也是很重要的.
@FedericoPeraltaSchaffner我编辑了代码以展示它是如何实现的.
@FedericoPeraltaSchaffner的&#39;为什么&#39;并不容易和老实说我只能想到更好地适应规范的情况(说不保证订单)......
@FedericoPeraltaSchaffner那里还有一段非常有趣的代码,*至少*对我来说.`ImmutableCollections.SetN` - 如何选择数组中的插槽...
2> Holger..:
这里有两个方面.正如尤金正确指出的那样,你不能假设一个HashSet
迭代顺序保持不变 - 没有这样的保证.
但另一方面是Stream
当Spliterator
不报告ORDERED
特征时不需要维持迭代顺序的实现.
换句话说,如果流是无序的,skip(1)
则不需要跳过第一个元素,因为没有"first"元素,而只是跳过一个元素.
虽然流不太可能实现随机化,但它们试图利用特征来最小化工作.一个看似合理的情况是,一个Stream
实现将处理skip(n)
一个无序但SIZED
源,就像这样limit(size-n)
也会有效地跳过n个元素,而工作量更少.
这样的优化可能不会在今天发生,但在下一个版本中,即使在HashSet
迭代顺序没有改变的情况下也会破坏批处理场景.
优点!我没有想到它,但它很有道理.