Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/386.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 具有无序终端操作的Stream.skip行为_Java_Parallel Processing_Java 8_Java Stream_Collectors - Fatal编程技术网

Java 具有无序终端操作的Stream.skip行为

Java 具有无序终端操作的Stream.skip行为,java,parallel-processing,java-8,java-stream,collectors,Java,Parallel Processing,Java 8,Java Stream,Collectors,我已经阅读并提出了一些问题,但仍然怀疑观察到的Stream.skip行为是否是JDK作者的意图 让我们简单输入数字1..20: List<Integer> input = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList()); 过滤步骤在这里基本上什么都不做,但给流引擎增加了更多的困难:现在它不知道输出的确切大小,因此关闭了一些优化。我有以下结果: skip-skip-unordered-toList:

我已经阅读并提出了一些问题,但仍然怀疑观察到的
Stream.skip
行为是否是JDK作者的意图

让我们简单输入数字1..20:

List<Integer> input = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
过滤步骤在这里基本上什么都不做,但给流引擎增加了更多的困难:现在它不知道输出的确切大小,因此关闭了一些优化。我有以下结果:

skip-skip-unordered-toList: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
// absent values: 1, 2
skip-unordered-skip-toList: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20]
// absent values: 1, 15
unordered-skip-skip-toList: [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20]
// absent values: 7, 18
结果很好,一切正常。在第一种情况下,我要求跳过前两个元素,然后收集并没有特定顺序的列表。在第二种情况下,我要求跳过第一个元素,然后变成无序,再跳过一个元素(我不在乎是哪一个)。在第三种情况下,我首先进入无序模式,然后跳过两个任意元素

让我们跳过一个元素,以无序模式收集到自定义集合。我们的自定义集合将是一个
哈希集

System.out.println("skip-toCollection: "
        + input.parallelStream().filter(x -> x > 0)
        .skip(1)
        .unordered()
        .collect(Collectors.toCollection(HashSet::new)));
System.out.println("skip-toSet: "
        + input.parallelStream().filter(x -> x > 0)
            .skip(1)
            .unordered()
            .collect(Collectors.toSet()));
结果令人满意:

skip-toCollection: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
// 1 is skipped
所以一般来说,我希望只要流是有序的,
skip()
跳过第一个元素,否则它跳过任意元素

但是,让我们使用等效的无序终端操作
collect(Collectors.toSet())

现在输出为:

skip-toSet: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20]
// 13 is skipped
任何其他无序终端操作(如
forEach
findAny
anyMatch
等)都可以获得相同的结果。在这种情况下,删除
unordered()
步骤不会改变任何内容。似乎当
unordered()
步骤正确地使流从当前操作开始无序时,unordered terminal操作使整个流从一开始就无序,尽管如果使用
skip()
,这可能会影响结果。这似乎完全误导了我:我认为使用无序收集器与在终端操作之前将流转换为无序模式并使用等效的有序收集器是一样的

因此,我的问题是:

  • 这种行为是故意的还是错误
  • 如果是,是否有文件记录?我读过文档:它没有提到任何无序的终端操作。另外,文档也不是很容易理解,也没有说整个流的顺序将丢失。最后,包摘要中的部分也不包括这种情况。也许我遗漏了什么
  • 如果无序的终端操作使整个流无序,为什么
    unordered()
    step仅从这一点开始使其无序?我能相信这种行为吗?或者我只是很幸运我的第一次测试很顺利
  • @鲁本,你可能不明白我的问题。大致上就是问题所在 is:unordered().collect(toCollection(HashSet::new))的行为原因 与collect(toSet())不同。我当然知道toSet()是 无序的

    可能吧,不过,无论如何,我会再试一次

    查看收集器toSet和toCollection的Javadocs,我们可以看到toSet提供了一个无序的收集器

    这是一个{@link Collector.Characteristics#无序无序} 收藏家

    i、 例如,具有无序特征的CollectorImpl。查看Collector.Characteristics的Javadoc#无序我们可以阅读:

    指示收集操作未提交保存 输入元素的相遇顺序

    在Collector的Javadocs中,我们还可以看到:

    对于并发收集器,实现是免费的(但不是免费的) (需要)同时实施削减。同时减少 是从中并发调用累加器函数的函数 多个线程,使用相同的可并发修改的结果 容器,而不是在 积累只有在以下情况下,才应同时应用缩减: 收集器具有{@link Characteristics#UNORDERED}特性或 如果原始数据是无序的

    这对我来说意味着,如果我们设置无序特征,我们根本不关心流的元素传递到累加器的顺序,因此,可以从管道中以任何顺序提取元素

    顺便说一句,如果在示例中省略unordered(),您将得到相同的行为:

        System.out.println("skip-toSet: "
                + input.parallelStream().filter(x -> x > 0)
                    .skip(1)
                    .collect(Collectors.toSet()));
    
    此外,Stream中的skip()方法给了我们一个提示:

    而{@code skip()}通常是一种廉价的顺序操作 流管道,它可以是相当昂贵的有序并行 管道

    使用无序流源(例如{@link#generate(Supplier)}) 或者使用{@link#unordered()}删除排序约束可能会 导致显著的加速

    使用时

    Collectors.toCollection(HashSet::new)
    

    您正在创建一个正常的“有序”收集器(一个没有无序特征的收集器),对我来说,这意味着您确实关心排序,因此,元素是按顺序提取的,您可以获得预期的行为。

    回想一下流标志的目标(有序、排序、大小、不同)是为了使操作避免做不必要的工作。涉及流标志的优化示例包括:

    • 如果我们知道流已经排序,那么
      sorted()
      是不可操作的
    • 如果我们知道流的大小,我们可以在
      toArray()
      中预先分配一个大小正确的数组,避免复制
    • 如果我们知道输入没有有意义的遭遇顺序,我们就不需要采取额外的步骤来保持遭遇顺序
    管道的每个阶段都有一组流标志。中间操作可以注入、保留或清除流标志。例如,过滤保留已排序的模糊度/清晰模糊度,但不保留大小模糊度;映射保留大小不一致性,但不保留排序不一致性或明显不一致性。排序注入了有序性。治疗
    Collectors.toCollection(HashSet::new)
    
    set.stream()
       .sorted()
       .forEach(System.out::println);