我已经阅读了这个和这个问题,但仍然怀疑JDK作者是否有意让Stream.skip
表现出观察到的行为。
让我们使用数字1..20进行简单输入:
List<Integer> input = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
现在让我们创建一个并行流,在不同的方式中使用unordered()
和skip()
结合起来,并收集结果:
System.out.println("skip-skip-unordered-toList: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.skip(1)
.unordered()
.collect(Collectors.toList()));
System.out.println("skip-unordered-skip-toList: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.unordered()
.skip(1)
.collect(Collectors.toList()));
System.out.println("unordered-skip-skip-toList: "
+ input.parallelStream().filter(x -> x > 0)
.unordered()
.skip(1)
.skip(1)
.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
结果完全正常,一切都按预期工作。在第一种情况下,我要求跳过前两个元素,然后无序地收集到列表中。在第二种情况下,我要求跳过第一个元素,然后变成无序的并跳过另一个元素(我不关心哪一个)。在第三种情况下,我先变成无序模式,然后跳过两个任意元素。
让我们跳过一个元素,并以无序模式收集到自定义集合中。我们的自定义集合将是HashSet:System.out.println("skip-toCollection: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.unordered()
.collect(Collectors.toCollection(HashSet::new)));
输出结果令人满意: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())
:
System.out.println("skip-toSet: "
+ input.parallelStream().filter(x -> x > 0)
.skip(1)
.unordered()
.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()
步骤正确地使流从当前操作开始无序,而无序终端操作使整个流从最开始就变得无序,尽管如果使用了skip()
,这可能会影响结果。对我来说,这似乎完全是误导性的:我期望使用无序收集器与将流转换为无序模式 就在终端操作之前,并使用等效的有序收集器是相同的。
所以我的问题是:
- 这种行为是有意为之还是bug?
- 如果是,它是否有文档记录?我已阅读Stream.skip()文档:它没有关于无序终端操作的任何说明。此外Characteristics.UNORDERED 文档不太容易理解,也没有说整个流将失去排序。最后,在包概述中的Ordering部分也没有涵盖这种情况。也许我漏掉了什么?
- 如果无序终端操作使整个流变得无序,那么为什么
unordered()
步骤只在此点之后使其无序?我可以依赖这种行为吗?还是我只是运气好,我的第一次测试很顺利?
sorted().forEach()
不应该排序。 - Holgerboxed()
调用?我不能这样collect()
,需要boxed()
。 - Thomas Wellerboxed()
已添加。.parallelStream().filter(x -> x > 0)
是必要的,因为我想揭示问题,而不是消除它们 :-) 当然,这只是一个人工简化的例子。在实践中,如果您使用例如bufferedReader.lines().skip(1).parallel().forEach(...)
,可能会出现此类问题。请参见链接的问题。 - Tagir Valeevlines.stream().skip(1).parallel().blahblah
可能对你很有效。 - Tagir Valeev