当前位置: 首页 > 科技观察

深入理解Stream的原理分析

时间:2023-03-19 16:10:36 科技观察

今天先说说深入理解Stream的原理分析。流操作分类流操作可以分为两类:中间操作和末端操作。中间操作只会记录操作,只有结束操作才会触发真正的计算,可以理解为懒加载,这也是Stream在迭代计算大对象时如此高效的原因之一。中间操作分为有状态操作和无状态操作。无状态意味着元素的处理不受前面元素的影响。Stateful是指获取到所有元素后才能继续操作。这也比较容易理解。比如有状态的distinct()去重方法,你觉得它能不关心其他值吗?当然不是,他必须得到所有的元素才能知道当前迭代的元素是否重复。末端操作可分为短路操作和非短路操作。这个应该很好理解。短路是指某些元素满足条件才能得到最终结果;非短路意味着必须处理所有元素才能获得最终结果。如此细粒度划分的原因是因为底层对每种情况的处理方式不同。流结构分析让我们简单看一下下面这段代码:Listlist=newArrayList<>();//获取stream1Streamstream1=list.stream();//stream1通过filter后得到stream2Streamstream2=stream1.filter("lige"::equals);//stream1和stream2是同一个对象吗?System.out.println("stream1.equals(stream2)="+stream1.equals(stream2));System.out.println("stream1.classTypeName="+stream1.getClass().getTypeName());系统。out.println("stream2.classTypeName="+stream2.getClass().getTypeName());//result//stream1.equals(stream2)=false//stream1.classTypeName=java.util.stream.ReferencePipeline$Head//stream1.classTypeName=java.util.stream.ReferencePipeline$2显然,stream1和stream2不是同一个对象,它们不是同一个实现类。stream1的实现类是ReferencePipeline$Head,stream2的实现类是一个匿名内部类。让我们进一步分析它的源代码。所谓源码之下,无所遁形。我们再来看stream2:通过分析可以发现,stream2的实现类是StatelessOp,所以形成了这样一个结构。每个中间操作都会生成一个新的Stream。如果是无状态操作,实现类是StatelessOp,如果是有状态操作,实现类是StatefulOp。我们再来看一下它们之间的继承关系。下面说说核心Sink内部实现StreamAPI的本质,就是如何重载Sink的四个接口方法。我还是先举个例子:Listlist=newArrayList<>();list.add("zhangsan");list.add("ligeligelisilisililisilisilisi");list.add("wangwu");list.add("ligejishuligejishuligejishuligejishuligejishuligejishuligejishu");ListresultList=list.stream().filter(it->it.contains("li"))//1.只要包含li数据.filter(it->it.contains("lige"))//2.只要包含ligedata.map(String::toUpperCase)//3.进一步处理匹配数据,转为大写.map(String::toLowerCase)//4.对匹配数据进行进一步处理,转换为小写。收集(Collectors.toList());resultList.forEach(System.out::println);不管是filter方法,还是map方法,或者其他方法,我们进入源码层面,返回一个StatelessOp对象或者StatefulOp对象。于是创建了这样一个结构:但是它和Sink有什么关系呢?我们反过来看filter或者map的源码:直接返回一个匿名StatelessOp对象,实现opWrapSink方法。opWrapSink方法是传入一个sink对象,返回另一个sink对象。并且新的接收器对象具有对传递的接收器对象的引用。但是这段代码是做什么用的呢?什么时候触发的?别着急,让我们从collect(Collectors.toList())方法开始一步步深入。这里我们要知道传入xx方法的终端对象是ReduceOp,这个ReduceOp对象在makeSink的时候返回一个匿名的内部类ReducingSink对象。我们这里提到了makeSink,它返回一个匿名内部类ReducingSink对象。先执行warpSink,再执行copyInto。说白了就是先把Sink封装成链式Sink,然后遍历Sink链复制到result对象。这里的两个步骤非常核心。先看warpSink:第一次进入时,这是最后一个Stream对象。每次从尾部遍历到头部时,都会得到一个新的Stream对象。一般是StatelessOp对象或者StatefulOp对象执行操作对象的opWrapSink方法,是匿名实现的。在每一个opWrapSink的实现方法中,都会传入之前的sink,最后得到一个sink列表。最后返回Sink链的头节点,内部称为wrappedsink,取名wrapped。然后,准备执行begin、forEachRemaining、end方法。forEachRemaning最后调用accept方法。动画理解Stream执行过程