Stream流编程也是java8中的新特性。Stream是一个高级的迭代器,不是一个数据结构,不是一个集合,不会存放数据。它是将数据放在一个流水线中处理,在流水线的一边输入数据,在流水线的尾端得到结果,中间有一系列的操作。
外部迭代和内部迭代
先看代码:
1 | import java.util.stream.IntStream; |
外部迭代就是使用for、while等循环进行迭代的操作,串行的,如果数据量大要自己实现线程池等操作;内部迭代更加简短,只需要知道要做什么,并不需要知道内部实现细节。
中间操作/终止操作和惰性求值
1 | import java.util.stream.IntStream; |
中间操作是返回流的操作,只是操作中的一个步骤;终止操作产生的是一个结果(副作用)。
惰性求值就是终止操作没有调用的情况下,中间操作不会执行。在上面代码清单中,main函数最后一行IntStream.of(nums).map(StreamDemo1::doubleNum);
不会有输出,因为其没有终止操作。
流的创建
类型 | 相关方法 |
---|---|
集合 | Collection.stream/parallelStream |
数组 | Arrays.stream |
数字Stream | IntStream/LongStream.range/rangeClosed |
随机数Stream | Radom.ints/longs/doubles |
自己创建 | Stream.generate/iterate |
下面代码清单是一些简单的示例:
1 | import java.util.ArrayList; |
流的中间操作
流的中间操作都返回stream,大致有两类,一类是 有状态操作,表示结果需要依赖其他一些元素,例如排序依赖于其他操作都计算完毕才能有排序结果;一类是无状态操作,表示当前操作与前后无依赖关系。
方法 | 说明 |
---|---|
map/mapToInt/Long… | 无状态操作,对每个元素执行操作 |
flatMap/flatMapToInt/Long… | 无状态操作,对每个元素的一个属性执行操作 |
filter | 无状态操作,过滤 |
peek | 无状态操作,类似于foreach |
unordered | 无状态操作,多用于并行流,无序 |
distinct | 有状态操作,去重 |
sorted | 有状态操作,排序 |
limit/skip | 有状态操作,limit是无限流的限制,skip是跳过一些数据 |
下面代码清单是一些示例:
1 | import java.util.Random; |
流的终止操作
终止操作分为两类,一类是非短路操作,;一类是短路操作,短路操作是一些不用等结果全部计算完就可以结束流的操作。
方法 | 说明 |
---|---|
forEach/forEachOrdered | 非短路操作,遍历 |
collect/toArray | 非短路操作,收集到集合或数组 |
reduce | 非短路操作,归约 |
min/max/count | 非短路操作,最大值,最小值,计数 |
findFirst/findAny | 短路操作,找到第一个或任意一个 |
allMatch/anyMatch/noneMatch | 短路操作,匹配 |
下面代码清单是一些示例:
1 | import java.util.List; |
并行流
使用Stream流编程有个非常重要的特点,就是可以非常方便的使用并行流,而不需要自己去管理多线程,自己去拆分任务。
1 | import java.util.concurrent.TimeUnit; |
我们发现,使用并行流只需要在调用的时候加一个parallel()
方法即可(串行流使用sequential()
),运行时,我的电脑会一次打印4个数字(乱序),表示当前有4个线程。有两个注意点,第一,多次调用parallel()
和sequential()
,以最后一次调用为准;第二,多线程运行时,我们发现并行流使用的线程池是:ForkJoinPool.commonPool
,默认线程数时当前电脑的cpu核数(我的为4核,因此有4个)。
修改默认的线程数的方法,下面代码清单是修改为20个线程:
1 | //在main函数中调用并行流的代码之前加上下面代码即可。 |
还有一个问题,所有的并行流都使用同一个默认的并行池会形成阻塞,假设现在增加了一些任务并行流,但是由于默认线程池有其他的一些并行任务在处理,那么这个任务可能会处理的非常非常晚,这是不可预知的。那么我们可以让并行流使用自己的线程池,而不使用默认的线程池,以防止任务被阻塞。下面代码清单是使用自己创建的线程池示例:
1 |
|
收集器
收集器是Stream流编程里面一个非常重要以及非常有用的知识点。收集器的意思就是把流处理后的东西收集起来,可以收集到一些集合类,比如List、Set、Map等等,或者把最后处理后的数据再处理成一条,比如求和,字符串拼接等。下面代码清单是一些常用的收集器的用法:
1 | import org.apache.commons.collections4.MapUtils; |