深入理解Stream之原理剖析

网站建设4年前发布
76 0 0

今天我们先来聊聊深入理解Stream之原理剖析。,202303060955127974bb699ea0df8e4c613622f1c7eb7d819d69654,202303060149503731da0319f19cfa3e6125d940604609a28eb5458,Stream中的操作可以分为两大类:中间操作与结束操作。,中间操作只会进行操作记录,只有结束操作才会触发实际的计算,可以理解为懒加载,这也是Stream在操作大对象迭代计算的时候如此高效的原因之一。,中间操作分为有状态操作与无状态操作,无状态是指元素的处理不受之前元素的影响,有状态是指该操作只有拿到所有元素之后才能继续下去。这也比较好理解,比如有状态的distinct()去重方法,你说他能不关心其他值吗?当然不能,他必须拿到所有元素才知道当前迭代的元素是否被重复。,结束操作可以分为短路与非短路操作,这个应该很好理解,短路是指遇到某些符合条件的元素就可以得到最终结果;而非短路是指必须处理所有元素才能得到最终结果。,之所以要进行如此精细的划分,是因为底层对每一种情况的处理方式不同。,让我们先简单看看下面一段代码:,很明显,stream1与stream2不是同一个对象,并且他们不是同一个实现类。stream1的实现类为ReferencePipeline$Head,而stream2的实现类为一个匿名内部类,让我们进步一分析其源码,所谓源码之下,无所遁形。,20230306014951f3d383801d68d3fff3e8609631b73ad5a4a6f5833,20230306014951a92fef0146d5b4081ae4517c33060d1dcc91a3333,20230306014952e85ddb521ed023aa92e805957f7deff43ca3b6336,让我们再看看stream2:,20230306014952715c91256051458f512678639cdd57d3ecc1af952,20230306015003777beaf621ad7e1515c135a8edb5652a41cbcb612,20230306014953920a3b036117259c765039fa31f4a109e6a58b183,20230306014954167006b01c034a896849208d40ba59329fb5e0469,2023030601495429f4d0495ab98c56fa9190a6c4af91d4806de4965,2023030601495512fc269768402fafe9c7502fe99deaf0b84e32670,通过分析我们可以发现,stream2的实现类是StatelessOp,所以就形成了这样一个结构。,20230306014955e32b638532deace8fa54271e3cac1243308514856,每一次中间操作都会生成一个新的Stream,如果是无状态操作则实现类是StatelessOp,如果是有状态操作则实现类是StatefulOp。,让我们再来看一下他们之间的继承关系。,20230306015003f11c00719e62207996f031e4cfc5f0502a1a63390,20230306015131c4d23e2617d11349b3976176f5b8ca392850fe501,实际上Stream API内部实现的的本质,就是如何重载Sink的这四个接口方法。,我还是从一个示例开始:,不管是filter方法,还是map方法,还是其他的方法,我们进入到源码层面,返回了一个StatelessOp对象或StatefulOp对象。,所以便产生了这样一个结构:,2023030601500537d5dac52412a44bc3828239cf86c3e64718a3485,但是和Sink有什么关系呢?我们再反过来看filter或者map源码:,20230306095513f141f82302f9453e8d0247d3dc4d9efd6a109c581,直接返回一个匿名StatelessOp对象,实现opWrapSink方法,opWrapSink方法是传入一个sink对象,返回另一个sink对象。而新的sink对象拥有传入sink对象的引用。,但是,这个代码有什么用?什么时候触发的呢?,别着急,让我们从collect(Collectors.toList())方法开始一步一步深入研究。,20230306015007d72bac891c0c8c8c3c27113b423c434955fccc574,2023030601500776504bb477f94056c47451687c9b60347ec35b259,202303060150071789c1c596e29759d5015847ff1d3ab484085f443,这里我们需要知道传入xx方法的终端对象是ReduceOp,并且这个ReduceOp对象在makeSink的时候返回了一个匿名内部类ReducingSink对象。,202303060151311256724455e83bc8e6f7217778dcfcae849a03326,2023030601513282d3d3a28d669ec864b3336f771ca8e351b730798,20230306015009839bdbe74480bdace7897559153a6ca630df68571,这里的makeSink我们提到过,返回一个匿名内部类ReducingSink对象。,2023030601500916e133c53e7071c0658493693f441bdf7500c1484,先执行warpSink,再执行copyInto。直白一点就是先对Sink进行包装成链式Sink,再遍历Sink链进行copy到结果对象里。这里的两个步骤都很核心。,先看warpSink:,20230306015009121c5fb849a00f8bd2763189662b07a9ee32be688,首次进入时,this为最后的Stream对象,从尾部向头部遍历,每次遍历时,得到一个新的Stream对象,一般为StatelessOp对象或StatefulOp对象,执行操作对象的opWrapSink方法,这就是匿名实现了。,在每一个opWrapSink实现方法中,传入了上一个sink,最终得到一个sink链表,20230306015133e7a8825921e1d7f664b25059c9398b783856fd827,最后,返回Sink链的头节点,内部称之为包装好的sink,命名wrapped,随后,准备进行执行begin,forEachRemaining,end方法。,2023030601501033eec8b495804753f7f927cccb1077f44ed243298,forEachRemaning最终调用accept方法。,20230306015011119686b25ad177e7c944451da1b28a89430aee685,动画理解Stream执行流程, 20230306095514f5c5ba6983125cc30a59399247218bf4b66f0e519

© 版权声明

相关文章