Java8流在订单

时间:2017-07-20 13:57:50

标签: lambda collections java-8 hashset

据我所知,Set in java是一个无序集合,迭代器将以其选择的某个顺序处理项目(我可能在这里错了)但确保它处理集合中的所有元素。

在Java8中,集合中的stream()API已经引入了跳过和限制功能。所以我想知道从流处理的项目的顺序是否保持相同,无论我开始流的次数或每次都是随机的?如果在流之间修改集合,订单会改变吗?

可能无关紧要,但我在这里提出问题: 现在遇到这个问题,我有一套2000或者其他什么东西在创建后不会被修改,我正在进行50次批处理操作,涉及每个批次的网络调用。我有一个启动参数,每次调用后增加50。如果我在我的Set上使用一个带有“start”的流作为每个批处理的skip参数,那么它对于每个批处理都是一个新流吗?因此,流程的顺序保持不变。显然,我不会多次相同的条目,更重要的是我不会错过任何条目。最简单的事情是对我来说是一个Arraylist,但我想知道我是否真的需要创建一个集合。

2 个答案:

答案 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不可变SetMap 确实有内部随机化和&#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的迭代顺序没有改变的情况下,也会破坏批处理场景。