Java 8新特性之流

前言

Java 8中另外一个重要的变化是新增了Stream,它可以对集合的查找,过滤等复杂操作进行简化处理,还可以并行执行操作,可以把它们看做遍历数据集的高级迭代器。StreamAPI对于代码所带来的好处是:声明性——更简洁,更易读;可复合——更灵活;可并行——性能更好。Java8中三种简单的重构代码方式:用Lambda表达式取代匿名内部类;用方法引用重构Lambda表达式;用StreamAPI重构命令式的数据处理。

一.流的定义

流是从支持数据处理操作的源生成的元素序列。
元素序列:流提供了一个特定接口,可以访问特定元素类型的一组有序值。集合是一种数据结构,它的主要目的是以特定的时间空间复杂度存储和访问元素,但流的目的是表达计算。所以集合说的是数据,流说的是计算。
:流会使用一个提供数据的源,如集合,数组,输入输出资源。从有序集合生成流时会保留原有的顺序,由列表生成的流,其元素顺序与列表一致。
数据处理操作:流的数据处理功能支持类似于数据库的操作及函数式编程语言中的操作,流操作可以顺序执行,也可以并行执行。
流水线:很多流操作会返回一个流,多个操作可以链接起来形成一个流水线,流水线的操作可以看做是对数据源进行数据库式的查询,还可以做一些优化,如延迟和短路。
内部迭代:与使用迭代器显式迭代的集合不同,流的迭代是在背后进行的。

二.流与集合

流与集合的区别在于什么时候进行计算,集合是内存中的数据结构,每个元素必须先算出来再添加到集合中,流是在概念上固定的数据结构,不能添加或删除元素,其元素是按需计算的,只有在消费者要求的时候才会计算值。和迭代器类似,流只能遍历一次,遍历完之后,这个流就已经被消费掉了,要想重新遍历一次,就要从原始数据那里再获得一个新的流。
流是内部迭代,它的好处是可以透明的并行处理,并选择更优化的顺序处理,Stream库可以自动选择一种适合你硬件的数据表示和并行实现。

三.Stream操作的步骤:

1. 创建一个Stream,如集合,数组,获取一个流
(1)从集合生成流
在这里插入图片描述
(2)由值创建流
在这里插入图片描述
(3)由数组创建流
在这里插入图片描述
(4)由文件生成流,IO操作
在这里插入图片描述
(5)由函数生成流,创建无限流
在这里插入图片描述
在这里插入图片描述
2. 中间操作,对数据源的数据进行操作
(1)collect(toList())

List<String> collected = Stream.of("a1","a2","a3").collect(Collectors.toList());

Collected的值:[a1, a2, a3],首先由列表生成一个Stream,然后
进行Stream的一些操作,再是collect操作,由Stream生成列表。
(2)Map
在这里插入图片描述
Map操作可以将一个流中的值转换成一个新的流,如图,Lambda表达式只接受一个String类型的参数,返回一个新的String,由于返回的是一个新的流,所以返回值的类型和参数类型不必一致。flatMap方法可以将多个Stream连成一个Stream,它和map的函数接口都是Function接口,只是方法的返回值是Stream类型。
(3)Filte
在这里插入图片描述
Filter和map有点像,也接受一个函数作为参数,该函数返回一个boolean值,而函数接口正是Predicate。
(4)Stream API中还有很多操作,如:
(a)筛选,切片:filter,distinct,skip,limit;
(b)提取或转换流中的元素:map,flatMap;
(c)查找流中的元素:findFirst,findAny;
(d)让流匹配给定的谓词:allMatch,noneMatch,anyMatch;这些方法都利用了短路求值,即有些操作不需要处理整个流就能得到结果,比如limit只需要创建一个给定大小的流,而不用处理流中所有的元素,它可以把无限流变成有限流。
(e)将流中所有元素迭代合并成一个结果:reduce;
(f)filter,map等并不存储任何状态,它们是无状态的,reduce,sorted,distinct等要存储一个状态才能计算出值,因为它们要把流中所有元素缓存起来才能返回一个新的流,是有状态的。(g)流有三种基本的原始类型特化,IntStream,DoubleStream,LongStream。
3.终止操作,执行完中间操作后获得一个结果。
中间操作可以将流链接起来,将一个流转换成另外一个流,这些操作不会消耗流,目的是建立一个流水线,终止操作会从中间操作中生成一个结果,会消耗流,结果不一定是流,可以是List,Integer,void,它们通常可以通过优化流水线来缩短计算时间。

四.并行数据处理

在java7以前,并行处理数据集合的方法是:把包含数据的数据结构分成若干子部分,给每个子部分分配一个独立的线程,并且要在恰当的时候对它们进行同步来避免多线程的竞争,等待所有线程完成,最后把这些部分结果合并起来。Java7引入了一个分支、合并的框架使这些操作更稳定。而Stream接口可以通过对收集源调用parallelStream方法来把集合转变成并行流,并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。它可以自动把给定操作的工作负荷分配给多核处理器的所有内核。
并行流内部使用了默认的ForkJoinPool(分支合并框架),它默认的线程数量就是计算机的处理器数量,这个值是由Runtime.getRuntime().avaliableProcessors()得到的。当然,并不建议通过系统属性去改变这个默认值。
简单介绍一下影响并行流性能的因素。
影响并行流性能的因素有5个:
1. 数据大小
将数据分解之后并行化处理在合并会带来额外的开销,只有数据足够大,每个数据处理管道花费的时间足够多时,并行化处理才有意义。
2. 数据源结构
将不同的数据源分割带来的性能开销影响了在管道中并行处理数据时到底能带来多少性能上的提升。
在这里插入图片描述
可分解性越好,使用并行流性能越好。
3. 装箱
处理基本类型比处理装箱类型要快。
4. 核的数量
单核没必要并行化。运行时机器能使用的核数越多,获得的潜在提升性能的幅度越大。
5,单元处理开销
花在流中每个元素身上的时间越长,并行带来的性能提升越明显。所以,流更注重的是对数据的计算,它们会返回一个持有结果的Stream,而不会影响到源对象,也就是说,它们会等到需要结果时才去执行,而且Stream本身不会存储元素。如limit,findFirst本身在并行流上的性能就比顺序流差。

上一篇:java 8新特性之Lambda表达式
下一篇:java 8新特性之收集器,Optional类

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值