当前位置: 首页 > 后端技术 > Java

JDK8Stream数据流效率如何?

时间:2023-04-01 23:06:44 Java

stream操作类型①中间操作②stream的终端操作特点①只能遍历一次:②内部迭代方式:stream相比Collection的优势,stream和iterator迭代效率相对较低1.Mapping处理测试2.Filter处理测试3.自然排序测试4.归约统计测试5.字符串拼接测试6.混合操作测试Stream是JavaSE8类库中一个新的重点抽象,定义在java.util.stream中(这里面有几个stream包中的类型:Stream代表对象引用流,还有一系列专门的流,比如IntStream、LongStream、DoubleStream等。Java8中引入的Stream主要用来替代一些Collection操作,而每个stream代表一个值序列,streams提供了一系列常用的聚合操作,可以方便的对其进行各种操作。集合类库也为我们以操作流的方式使用集合、数组等数据结构提供了方便;流操作的种类①中间操作。所有操作都称为“中间操作”;中间操作仍然返回一个流对象,因此可以将多个中间操作串联起来形成管道;stream提供各种类型的中间操作,如filter、distinct、map、sorted等;②终端操作当所有的中间操作都完成后,如果要从管道中取出数据,需要执行终端操作;对于终端操作,stream可以直接提供中间操作的结果,或者将结果转化为具体的集合、数组、String等;流的特点①只能遍历一次:数据流从一端获取数据源,在管道上依次对元素进行操作。数据源获取新的数据流进行操作;②采用内部迭代方式:对Collection进行处理,一般采用Iterator遍历器的遍历方式,即外部迭代;对于Stream的处理,只要声明处理方法,处理过程由流对象自己完成,是一种内部迭代。对于大量数据的迭代处理,内部迭代比外部迭代更高效;stream优于Collection的优点是没有存储:stream不存储值;流的元素源自数据源(可能是数据结构、生成函数或I/O通道等),通过一系列计算步骤获得;函数式风格:对流的操作会产生一个结果,但流的数据源不会被修改;理性评估:大多数流操作(包括过滤、映射、排序和去重)都可以延迟实现。这使得我们可以通过一次遍历完成整个流水线操作,并且可以提供更高效的短路操作实现;不需要上界:很多问题可以表示为无限流(infinitestream):用户不断读取流,直到出现满意的结果(例如,枚举完美数的操作可以表示为对所有整数进行过滤);收藏是有限的,但流可以表示为无限流;代码简洁:对于一些集合迭代处理操作,使用stream来写可以很简洁,如果使用传统的集合迭代操作,代码可能会非常冗长,可读性差;stream和iterator迭代的效率比较好,上面stream的优点都吹了这么多,stream函数的写法很舒服,那么steam的效率怎么样呢?先说结论:传统的iterator(for-loop)迭代性能比stream(JDK8)更高,尤其是在小数据量的情况下;在多核场景下,对于大数据量的处理,parallelstream可以比iterator有更高的迭代处理效率;我分别进行了映射、过滤、排序、归约统计、将字符串转换为随机数列表List(数量范围为10到10,000,000),统计了使用stream和iterator实现的运行效率,并测试了代码benchmark代码链接测试环境如下:系统:Ubuntu16.04xenialCPU:IntelCorei7-8550URAM:16GBJDKversion:1.8.0_151JVM:HotSpot(TM)64-BitServerVM(build25.151-b12,mixedmode)JVMSettings:-Xms1024m-Xmx6144m-XX:MaxMetaspaceSize=512m-XX:ReservedCodeCacheSize=1024m-XX:+UseConcMarkSweepGC-XX:SoftRefLRUPolicyMSPerMB=1001。映射处理测试将随机数列(List)中的每个元素加1后,重新组装成一个新的List,被测随机数列表的容量为10-10000000,运行10次取平均时间;//streamListresult=list.stream().mapToInt(x->x).map(x->++x).boxed().collect(Collectors.toCollection(ArrayList::new));//iteratorListresult=newArrayList<>();for(Integere:list){result.add(++e);}//parallelstreamListresult=list.parallelStream().mapToInt(x->x).map(x->++x).boxed().collect(Collectors.toCollection(ArrayList::new));2.过滤处理测试取出一个大于200的随机数列表(List)元素,组装成一个新的List,随机测试序列的容量为10-10000000,运行10次取平均时间;//streamListresult=list.stream().mapToInt(x->x).filter(x->x>200).boxed().collect(Collectors.toCollection(ArrayList::new));//iteratorListresult=newArrayList<>(list.size());for(Integere:list){if(e>200){result.add(e);}}//并行streamListresult=list.parallelStream().mapToInt(x->x).filter(x->x>200).boxed().collect(Collectors.toCollection(ArrayList::new));3。自然排序测试一个随机数列(List)自然排序组装成一个新的List,迭代器使用的是Collections#sortAPI(使用归并排序算法实现),随机数的容量测试序列为10-10000000,运行10次后取平均时间;//streamListresult=list.stream().mapToInt(x->x).sorted().boxed().collect(Collectors.toCollection(ArrayList::new));//iteratorListresult=newArrayList<>(list);Collections.sort(result);//并行streamListresult=list.parallelStream().mapToInt(x->x).sorted().boxed().collect(Collectors.toCollection(ArrayList::new));4、归约统计测试得到一个随机序列(List)的最大值,测试的随机数序列容量从10-10000000,运行10次后取平均时间;//streamintmax=list.stream().mapToInt(x->x).max()。getAsInt();//iteratorintmax=-1;for(Integere:list){if(e>max){max=e;}}//并行流max=list.parallelStream().mapToInt(x->x).max().getAsInt();5.字符串拼接测试获取一个随机的数字序列(List),每个元素之间用“,”分隔String,测试随机序列的容量为10-10000000,运行10次后取平均时间;//streamStringresult=list.stream().map(String::valueOf).collect(Collectors.joining(","));//iteratorStringBuilderbuilder=newStringBuilder();for(Integere:list){builder.append(e).append(",");}Stringresult=builder.length()==0?"":builder.substring(0,builder.length()-1);//并行streamStringresult=list.stream().map(String::valueOf).collect(Collectors.joining(","));6.混合运算测试去除空值,去重,映射,过滤,将一个随机数序列(List)拼装成一个新的List。测试的随机数序列容量从10到10000000,运行10次后取平均时间;//streamListresult=list.stream().filter(Objects::nonNull).mapToInt(x->x+1).filter(x->x>200).distinct().boxed().collect(Collectors.toCollection(ArrayList::new));//iteratorHashSetset=newHashSet<>(list.size());for(Integere:list){if(e!=null&&e>200){set.add(e+1);}}列表<整数r>result=newArrayList<>(set);//并行streamListresult=list.parallelStream().filter(Objects::nonNull).mapToInt(x->x+1).filter(x->x>200).distinct().boxed().collect(Collectors.toCollection(ArrayList::new));实验结果总结从以上实验可以总结出以下几点:在场景(size<=1000)下,流的处理效率没有传统迭代器和外部迭代器快,但实际上,这些处理任务本身的运行时间不到毫秒。这种效率差距对于普通企业来说几乎是微不足道的。相反,stream可以让代码更加简洁;当数据量很大时(szie>10000),stream的处理效率会比iterator高,尤其是使用parallelstream的时候,CPU只是把线程分配给多核。(当然并行流底层使用了JVM的ForkJoinPool,分配线程本身就很玄学),可以达到很高的运行效率。但是在实际的一般业务中,一般不会有迭代次数高于10000次的计算;ParallelStream引用的CPU环境影响很大。当没有分配多个CPU核心时,加上引用forkJoinPool的开销,运行效率可能不如普通Stream;使用Stream的建议是迭代逻辑简单,可以直接使用iterator。对于多步处理的迭代逻辑,可以使用stream,几乎不损失效率,换来代码的高可读性是值得的;在单核cpu环境下,不推荐使用parallelstream,在多核cpu,数据量大的情况下,推荐使用parallelstream;流中包含装箱类型,在进行中间操作之前,最好将其转换为相应的数值流,以减少频繁拆箱装箱带来的性能损失;