在查看WrappingSpliterator::trySplit
的源代码时,我对其实现很误解:
@Override
public Spliterator<P_OUT> trySplit() {
if (isParallel && buffer == null && !finished) {
init();
Spliterator<P_IN> split = spliterator.trySplit();
return (split == null) ? null : wrap(split);
}
else
return null;
}
如果您想知道为什么这很重要,是因为例如:
Arrays.asList(1,2,3,4,5)
.stream()
.filter(x -> x != 1)
.spliterator();
正在使用它。以我的理解,在流中添加任何中间操作都将导致该代码被触发。
基本上,此方法表示除非流是并行的,否则将此Spliterator视为根本无法拆分的分离器。这对我很重要。在我的一种方法(这就是我获得该代码的方式)中,我得到了Stream
作为输入,并使用trySplit
手动将其“解析”成小块。例如,您可以认为我正在尝试从findLast
做一个Stream
。
在这里,我想要分裂成小块的愿望被否定了,因为我一这样做:
Spliterator<T> sp = stream.spliterator();
Spliterator<T> prefixSplit = sp.trySplit();
我发现prefixSplit
是null
,这意味着除了使用sp
消耗整个forEachRemaning
之外,我基本上不能做其他事情。
这有点奇怪,可能是 对于出现filter
时是有道理的;因为在这种情况下(据我所知),返回Spliterator
的唯一方法是使用某种buffer
,甚至可能具有预定义的大小(与Files::lines
相似)。但是为什么这样:
Arrays.asList(1,2,3,4)
.stream()
.sorted()
.spliterator()
.trySplit();
返回null
是我不了解的事情。 sorted
是一个有状态操作,无论如何都将缓冲元素,而实际上并没有减少或增加它们的初始数目,因此至少在理论上,这可以返回null
...
答案 0 :(得分:1)
在spliterator()
上调用Stream
时,当前的实现只有两种可能的结果。
如果流没有中间操作,您将获得用于构造流的源拆分器,其拆分能力完全独立于流的并行状态,实际上,拆分器不了解任何信息流。
否则,您将获得WrappingSpliterator
,它将封装源Spliterator
和管道状态,表示为PipelineHelper
。 Spliterator
和PipelineHelper
的这种组合不需要并行工作,实际上,在distinct()
的情况下是行不通的,因为WrappingSpliterator
将会完全不同组合,具体取决于Stream是否并行。
对于无状态的中间操作,这没有什么区别。但是,正如“ Why the tryAdvance of stream.spliterator() may accumulate items into a buffer?”中讨论的那样,WrappingSpliterator
是“万能的实现”,它没有考虑管道的实际性质,因此其局限性是所有可能的超集所有支持的管道阶段的局限性。因此,存在一种在忽略parallel
标志时不起作用的情况,足以禁止在没有parallel
时对所有管道进行拆分。