我偶然发现了一个相当恼人的问题:我有一个包含大量数据源的程序,它能够传输相同类型的元素,我希望" map"程序中的每个可用元素(元素顺序并不重要)。
因此,我尝试使用Stream<Stream<T>> streamOfStreamOfT;
Stream<T> streamOfT;
简化为简单的streamOfT = streamOfStreamOfT.reduce(Stream.empty(), Stream::concat);
由于元素顺序对我来说并不重要,我尝试将reduce操作与.parallel()
并行化streamOfT = streamOfStreamOfT.parallel().reduce(Stream.empty(), Stream::concat);
但是这会触发java.lang.IllegalStateException: stream has already been operated upon or closed
通过评论/取消注释.parallel()
public static void main(String[] args) {
// GIVEN
List<Stream<Integer>> listOfStreamOfInts = new ArrayList<>();
for (int j = 0; j < 10; j++) {
IntStream intStreamOf10Ints = IntStream.iterate(0, i -> i + 1)
.limit(10);
Stream<Integer> genericStreamOf10Ints = StreamSupport.stream(
intStreamOf10Ints.spliterator(), true);
listOfStreamOfInts.add(genericStreamOf10Ints);
}
Stream<Stream<Integer>> streamOfStreamOfInts = listOfStreamOfInts
.stream();
// WHEN
Stream<Integer> streamOfInts = streamOfStreamOfInts
// ////////////////
// PROBLEM
// |
// V
.parallel()
.reduce(Stream.empty(), Stream::concat);
// THEN
System.out.println(streamOfInts.map(String::valueOf).collect(
joining(", ")));
}
有人可以解释这个限制吗? /找到一种更好的方法来处理流的并行减少
关注@Smutje和@LouisWasserman评论.flatMap(Function.identity())
似乎是容忍.parallel()
流的更好选择
答案 0 :(得分:7)
您正在使用的reduce
形式采用标识值和关联合并功能。但Stream.empty()
不是一个值;它有州。流不是数组或集合之类的数据结构;它们是通过可能并行的聚合操作来推送数据的载体,它们具有一些状态(比如流是否被消耗。)想想它是如何工作的;你要构建一棵树,其中同一个“空”流出现在多个叶子中。当你尝试两次使用这个有状态的非同一性(它不会顺序发生,但会并行发生)时,第二次尝试遍历那个空流时,它将被正确地看作已被使用。
所以问题是,你只是错误地使用这个reduce
方法。问题不在于并行性;简单来说,并行性暴露了潜在的问题。
其次,即使按照你认为应该的方式“工作”,你也只能建立代表扁平流的树的并行性;当你去加入时,那是一个连续的流管道。糟糕!
第三,即使按照你认为应该的方式“工作”,你也会通过构建连接流来增加很多元素访问开销,而你却不会从并行性中获益你在寻找。
简单的答案就是压缩流:
String joined = streamOfStreams.parallel()
.flatMap(s -> s)
.collect(joining(", "));