我正在学习java 8流,并且有些问题转向我。
假设此代码:
new Random().ints().forEach(System.out::println);
在内部某处,它调用IntPipeline
,我认为它有责任无限期地生成这些内容。通过查看java源代码很难理解Streams实现。
您能否给出一个简短的解释,或者提供一些关于如何生成流以及如何连接管道操作的好/易理解的材料。整数上面的代码示例是随机生成的,如何建立这种连接?
答案 0 :(得分:5)
Stream实现分为Spliterator
(输入特定代码)和管道(与输入无关的代码)。 Spliterator
与Iterator
类似。主要区别如下:
它可以将自己分成两部分(trySplit
方法)。对于有序的分裂器,部件是前缀和后缀(例如,对于阵列,它可以是前半部分和后半部分)。对于无序源(如随机数),两个部分都可以生成一些元素。由此产生的部分能够进一步分裂(除非它们变得太小)。此功能对于并行流处理至关重要。
它可以准确或估计其大小。确切的大小可用于为某些流操作(如toArray()
)预分配内存,或仅将其返回给调用者(如Java-9中的count()
)。估计的大小用于并行流处理,以决定何时停止拆分。
它可以报告一些特征,如ORDERED,SORTED,DISTINCT等。
它实现了内部迭代:代替两个方法hasNext
和next
,您只需要执行提供的tryAdvance
一次方法Consumer
,除非没有更多剩下的元素。
还有Spliterator
界面(Spliterator.OfInt
等)的原始专精,可以帮助您处理原始值,例如int
,long
或double
有效。
因此,要创建自己的Stream数据源,您必须实现Spliterator
,然后调用StreamSupport.stream(mySpliterator, isParallel)
为原始专业化创建Stream
和StreamSupport.int/long/doubleStream
。实际上Random.ints
实际上StreamSupport.intStream
调用Stream
提供了自己的分裂器。您不必自己实施所有Stream
操作。通常,{JD}中的每个流类型的AbstractPipeline
接口只针对不同的源实现一次。有基本的抽象类ReferencePipeline
和四个实现(Stream
为IntPipeline
,IntStream
为LongPipeline
,{{1}为LongStream
和DoublePipeline
的{{1}}。但是您有更多来源(DoubleStream
,Collection.stream()
,Arrays.stream()
,IntStream.range
,String.chars()
,BufferedReader.lines()
,Files.lines()
和等等,甚至更多地出现在Java-9中。所有这些来源都是使用自定义分裂器实现的。实现Random.ints()
比实现整个流管道要简单得多(特别是考虑到并行处理),所以这种分离是有道理的。
如果您想创建自己的流源,可以开始扩展AbstractSpliterator
。在这种情况下,您只需实现Spliterator
并调用超类构造函数,提供估计的大小和一些特征。 tryAdvance
通过将源的一部分读入数组(调用实现的AbstractSpliterator
方法)并为此前缀创建基于数组的spliterator来提供默认拆分行为。当然,这种策略性能不是很高,而且往往只提供有限的并行性,但作为一个起点,它还可以。稍后您可以自己实施tryAdvance
,提供更好的分割策略。