流的管道如何在像IntPipeline这样的java中工作

时间:2015-10-04 17:09:55

标签: java java-8 java-stream

我正在学习java 8流,并且有些问题转向我。

假设此代码:

 new Random().ints().forEach(System.out::println);

在内部某处,它调用IntPipeline,我认为它有责任无限期地生成这些内容。通过查看java源代码很难理解Streams实现。

您能否给出一个简短的解释,或者提供一些关于如何生成流以及如何连接管道操作的好/易理解的材料。整数上面的代码示例是随机生成的,如何建立这种连接?

1 个答案:

答案 0 :(得分:5)

Stream实现分为Spliterator(输入特定代码)和管道(与输入无关的代码)。 SpliteratorIterator类似。主要区别如下:

  • 它可以将自己分成两部分(trySplit方法)。对于有序的分裂器,部件是前缀和后缀(例如,对于阵列,它可以是前半部分和后半部分)。对于无序源(如随机数),两个部分都可以生成一些元素。由此产生的部分能够进一步分裂(除非它们变得太小)。此功能对于并行流处理至关重要。

  • 它可以准确或估计其大小。确切的大小可用于为某些流操作(如toArray())预分配内存,或仅将其返回给调用者(如Java-9中的count())。估计的大小用于并行流处理,以决定何时停止拆分。

  • 它可以报告一些特征,如ORDERED,SORTED,DISTINCT等。

  • 它实现了内部迭代:代替两个方法hasNextnext,您只需要执行提供的tryAdvance一次方法Consumer,除非没有更多剩下的元素。

还有Spliterator界面(Spliterator.OfInt等)的原始专精,可以帮助您处理原始值,例如intlongdouble有效。

因此,要创建自己的Stream数据源,您必须实现Spliterator,然后调用StreamSupport.stream(mySpliterator, isParallel)为原始专业化创建StreamStreamSupport.int/long/doubleStream。实际上Random.ints实际上StreamSupport.intStream调用Stream提供了自己的分裂器。您不必自己实施所有Stream操作。通常,{JD}中的每个流类型的AbstractPipeline接口只针对不同的源实现一次。有基本的抽象类ReferencePipeline和四个实现(StreamIntPipelineIntStreamLongPipeline,{{1}为LongStreamDoublePipeline的{​​{1}}。但是您有更多来源(DoubleStreamCollection.stream()Arrays.stream()IntStream.rangeString.chars()BufferedReader.lines()Files.lines()和等等,甚至更多地出现在Java-9中。所有这些来源都是使用自定义分裂器实现的。实现Random.ints()比实现整个流管道要简单得多(特别是考虑到并行处理),所以这种分离是有道理的。

如果您想创建自己的流源,可以开始扩展AbstractSpliterator。在这种情况下,您只需实现Spliterator并调用超类构造函数,提供估计的大小和一些特征。 tryAdvance通过将源的一部分读入数组(调用实现的AbstractSpliterator方法)并为此前缀创建基于数组的spliterator来提供默认拆分行为。当然,这种策略性能不是很高,而且往往只提供有限的并行性,但作为一个起点,它还可以。稍后您可以自己实施tryAdvance,提供更好的分割策略。