stream.parallel().skip(1)
vs
stream.skip(1).parallel()
这是关于Java 8流的 这两个都跳过了第一行/条目吗?
示例是这样的:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.concurrent.atomic.AtomicLong;
public class Test010 {
public static void main(String[] args) {
String message =
"a,b,c\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n1,2,3\n4,5,6\n7,8,9\n";
try(BufferedReader br = new BufferedReader(new StringReader(message))){
AtomicLong cnt = new AtomicLong(1);
br.lines().parallel().skip(1).forEach(
s -> {
System.out.println(cnt.getAndIncrement() + "->" + s);
}
);
}catch (IOException e) {
e.printStackTrace();
}
}
}
今天早些时候,我有时会得到标题行" a,b,c"在lambda表达式中。这是一个惊喜,因为我期待已经跳过它。现在我无法让这个例子工作,即我无法获得lambda表达式中的标题行。所以我现在很困惑,也许其他东西正在影响这种行为。当然这只是一个例子。在现实世界中,正在从CSV文件中读取消息。该消息是该CSV文件的完整内容。
答案 0 :(得分:3)
是的,但skip(n)
较慢,因为并行流的n
较大。
以下是来自skip()
的API说明:
虽然
skip()
通常是顺序流管道上的廉价操作,但在有序并行管道上可能非常昂贵,特别是对于n
的大值,因为skip(n)
被限制为跳过不仅仅是n
元素,还有遇到订单中的第一个n
元素。如果您的情境的语义允许,使用无序流源(例如generate(Supplier)
)或使用BaseStream.unordered()
删除排序约束可能会导致并行管道中skip()
的显着加速。如果需要与遭遇顺序保持一致,并且您在并行管道中使用skip()
时性能或内存利用率较低,则切换到使用BaseStream.sequential()
执行顺序执行可能会提高性能。
基本上,如果您希望使用skip()
获得更好的性能,请不要使用parellel流,或使用无序流。
至于它似乎不适用于并行流,或许您实际上看到元素不再被排序?例如,此代码的输出:
Stream.of("Hello", "How", "Are", "You?")
.parallel()
.skip(1)
.forEach(System.out::println);
时
是否
你?
如何
这很好,因为forEach
不会在并行流中强制执行遭遇顺序。如果您希望它强制执行遭遇顺序,请使用顺序流(并且可能使用forEachOrdered
以便您的意图明显)。
Stream.of("Hello", "How", "Are", "You?")
.skip(1)
.forEachOrdered(System.out::println);
如何
是
你呢?
答案 1 :(得分:3)
你实际上有两个问题,第一个是在编写stream.parallel().skip(1)
还是stream.skip(1).parallel()
时是否有所不同,第二个问题是其中一个或两个是否总是跳过第一个元素。另请参阅“loaded question”。
第一个答案是没有区别,因为指定.sequential()
或.parallel()
执行策略会影响整个Stream管道,无论您将它放在调用链中的哪个位置 - 当然,除非你指定了多个矛盾的政策,在这种情况下,最后一个政策获胜。
因此,在任何一种情况下,您都在请求并行执行,这可能会影响skip
操作的结果,这是第二个问题的主题。
答案并非那么简单。如果Stream首先没有定义的遭遇顺序,则可能会跳过任意元素,这是因为没有“first”元素,即使在迭代时可能存在首先遇到的元素源。
如果你有一个有序的流,skip(1)
应跳过第一个元素,但这只是最近才制定的。正如“Stream.skip behavior with unordered terminal operation”中所讨论的,链接无序终端操作会对早期实现中的skip
操作产生影响,并且在“Is this a bug in Files.lines(), or am I misunderstanding something about parallel streams?”中可以看到这是否有意甚至是有意的,这恰好接近你的代码;显然,跳过第一行是一种常见的情况。
最后一句话是,早期JRE的行为是一个错误,有序流上的skip(1)
应该跳过第一个元素,即使并行执行流管道并且终端操作是无序的。 associated bug report将jdk1.8.0_60命名为第一个固定版本,我可以验证。因此,如果您使用较旧的实现,那么可能在使用.parallel()
和无序.forEach(…)
终端操作时会遇到跳过不同元素的Stream。如果实现偶尔会跳过预期的元素,这就是多线程的不可预测性,这并不矛盾。
所以答案仍然是stream.parallel().skip(1)
和stream.skip(1).parallel()
具有相同的行为,即使在早期版本中使用时也是如此,因为两者在与{{1}等无序终端操作一起使用时同样不可预测}}。他们应该总是跳过带有序Streams的第一个元素,当与forEach
或更新版本一起使用时,它们会这样做。