Lambda Java8流在一组顺序一致性上

Lambda Java8流在一组顺序一致性上,lambda,collections,java-8,hashset,Lambda,Collections,Java 8,Hashset,据我所知,java中的Set是一个无序的集合,迭代器将按照其选择的特定顺序处理这些项(这里我可能错了),但确保它处理集合中的所有元素 在Java8中,集合中的stream()API引入了跳过和限制功能。所以我想知道,不管我启动流多少次,流中处理的项目的顺序是否保持不变,还是每次都是随机的?如果在流之间修改集合,顺序会改变吗 可能与此无关,但我在此提出问题: 现在来解决这个问题,我有一套大小为2000的东西,在创建后不会被修改,我正在做一个50的批处理操作,涉及到每个批的网络调用。我有一个star

据我所知,java中的Set是一个无序的集合,迭代器将按照其选择的特定顺序处理这些项(这里我可能错了),但确保它处理集合中的所有元素

在Java8中,集合中的stream()API引入了跳过和限制功能。所以我想知道,不管我启动流多少次,流中处理的项目的顺序是否保持不变,还是每次都是随机的?如果在流之间修改集合,顺序会改变吗

可能与此无关,但我在此提出问题:
现在来解决这个问题,我有一套大小为2000的东西,在创建后不会被修改,我正在做一个50的批处理操作,涉及到每个批的网络调用。我有一个start参数,它在每次批处理调用时递增50次。如果我在我的集合中使用一个带有“start”的流作为每个批的跳过参数,那么它将是每个批的一个新流,对吗?因此,gaurenteed流的顺序保持不变。显然,我不会多次重复同一个条目,更重要的是,我不会错过任何条目。最简单的方法是创建Arraylist,但我想知道创建集合是否真的需要它。

让我们从一个示例开始。首先,我认为显而易见的一点是:

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);
允许打印以下内容:

 [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%的零和一分布,所以它不会改变结果

如何使用(例如,对于两个元素的
集合
):

我对jdk9内部随机化部分的猜测最初来自相关部分:

最后一个安全特性是不可变集合元素和映射键的随机迭代顺序。HashSet和HashMap迭代顺序一直未指定,但相当稳定,导致代码无意中依赖于该顺序。这会导致迭代顺序发生变化时出现问题,这种情况偶尔会发生。新的集合/映射集合从一个运行到另一个运行改变了它们的迭代顺序,希望在测试或开发的早期清除顺序依赖关系


因此,基本上就是打破所有依赖于
集合
/
映射
顺序的代码。当人们从java-7迁移到java-8并依赖HashMap的顺序(
LinkedNode
s)时也发生了同样的事情,这与
TreeNode
s的引入不同。如果你离开了这样一个功能,而人们依赖它多年——很难删除它并执行一些优化——比如HashMap移到
TreeNode
s;因为现在你被迫维持秩序,即使你不想。但这显然只是一个猜测,请这样对待它。

这里有两个方面。因此,您不能假设
HashSet
的迭代顺序保持不变,因为没有这样的保证

但是另一个方面是
实现,当
拆分器
不报告
有序的
特性时,不需要它来维护迭代顺序

换句话说,如果流是无序的,
skip(1)
不需要跳过第一个元素,因为没有“first”元素,只需要跳过一个元素

虽然流不太可能实现随机化,但它们试图利用特征来最小化工作。一种可能的情况是
实现将
跳过(n)
处理无序但
大小的
源代码,就像
限制(大小-n)
一样,这也将有效地跳过n个元素,工作量更少


这样的优化今天可能不会发生,但在下一个版本中,即使在
HashSet
的迭代顺序不变的情况下,也会破坏批处理场景。

@holi java总结我的问题,我想了解在多次执行set.stream()操作时是否保留了顺序。如果顺序经常变化,我不能真正依靠streamapi中的skip和limit方法对所有元素进行批处理。你不应该!也许今天您运行了一些测试,发现顺序得到了保留,但规范并不能保证这一点。如果明天jdk团队更改实现,您的代码可能会开始失败。如果你想保证秩序,你需要一个
列表
,这就是它们的目的。@FedericoPeraltaSchaffner同意。不管测试结果如何,这都不是高伦蒂德所说的。使用它太冒险了。我将为此创建一个ArrayList。请参阅和。@FedericOperaltachaffner我编辑了代码以展示它是如何实现的。@FedericOperaltachaffner为什么不容易,老实说,我只能考虑更好地适应规范的情况(即没有订单保证)…@Federicoperaltachaffner还有一段代码非常有趣,至少对我来说是这样
ImmutableCollections.SetN
-数组中的插槽是如何选择的…@Eugene您对随机化动机的猜测是正确的。我记得当时在核心libs-dev邮件列表上读到了这一点。即使是OpenJDK测试代码也有很多依赖项,要解决这些依赖项需要做很多工作。他们希望避免再次落入相同的陷阱。关于迭代顺序的回答很好,但同样重要的是要强调,一旦流无序,流上的
跳过
限制
,无论如何都不会绑定到迭代顺序。非常好的一点!我没想过,但这很有道理。
/**
 * 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);
}
// 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;