# Stream API (下)
# Stream API 下
## Collector 收集
收集器用来将经过筛选、映射的流进行最后的整理,可以使得最后的结果以不同的形式展现。
`collect`方法即为收集器,它接收`Collector`接口的实现作为具体收集器的收集方法。
`Collector`接口提供了很多默认实现的方法,我们可以直接使用它们格式化流的结果;也可以自定义`Collector`接口的实现,从而定制自己的收集器。
### 归约
流由一个个元素组成,归约就是将一个个元素“折叠”成一个值,如求和、求最值、求平均值都是归约操作。
### 一般性归约
若你需要自定义一个归约操作,那么需要使用`Collectors.reducing`函数,该函数接收三个参数:
* 第一个参数为归约的初始值
* 第二个参数为归约操作进行的字段
* 第三个参数为归约操作的过程
## 汇总
Collectors类专门为汇总提供了一个工厂方法:`Collectors.summingInt`。
它可接受一 个把对象映射为求和所需int的函数,并返回一个收集器;该收集器在传递给普通的`collect`方法后即执行我们需要的汇总操作。
### 分组
数据分组是一种更自然的分割数据操作,分组就是将流中的元素按照指定类别进行划分,类似于SQL语句中的`GROUPBY`。
### 多级分组
多级分组可以支持在完成一次分组后,分别对每个小组再进行分组。
使用具有两个参数的`groupingBy`重载方法即可实现多级分组。
* 第一个参数:一级分组的条件
* 第二个参数:一个新的`groupingBy`函数,该函数包含二级分组的条件
**Collectors 类的静态工厂方法**
待添加。。。
### 转换类型
有一些收集器可以生成其他集合。比如前面已经见过的`toList`,生成了`java.util.List`类的实例。
还有`toSet`和`toCollection`,分别生成`Set`和`Collection`类的实例。
到目前为止, 我已经讲了很多流上的链式操作,但总有一些时候,需要最终生成一个集合——比如:
* 已有代码是为集合编写的,因此需要将流转换成集合传入;
* 在集合上进行一系列链式操作后,最终希望生成一个值;
* 写单元测试时,需要对某个具体的集合做断言。
使用`toCollection`,用定制的集合收集元素
~~~
stream.collect(toCollection(TreeSet::new));
~~~
还可以利用收集器让流生成一个值。`maxBy`和`minBy`允许用户按某种特定的顺序生成一个值。
### 数据分区
分区是分组的特殊情况:由一个断言(返回一个布尔值的函数)作为分类函数,它称分区函数。
分区函数返回一个布尔值,这意味着得到的分组`Map`的键类型是`Boolean`,于是它最多可以分为两组: true是一组,false是一组。
分区的好处在于保留了分区函数返回true或false的两套流元素列表。
### 并行流
并行流就是一个把内容分成多个数据块,并用不不同的线程分别处理每个数据块的流。最后合并每个数据块的计算结果。
将一个顺序执行的流转变成一个并发的流只要调用`parallel()`方法
~~~
public static long parallelSum(long n){
return Stream.iterate(1L, i -> i +1).limit(n).parallel().reduce(0L,Long::sum);
}
~~~
将一个并发流转成顺序的流只要调用`sequential()`方法
~~~
stream.parallel().filter(...).sequential().map(...).parallel().reduce();
~~~
这两个方法可以多次调用,只有最后一个调用决定这个流是顺序的还是并发的。
并发流使用的默认线程数等于你机器的处理器核心数。
通过这个方法可以修改这个值,这是全局属性。
~~~
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "12");
~~~
并非使用多线程并行流处理数据的性能一定高于单线程顺序流的性能,因为性能受到多种因素的影响。
如何高效使用并发流的一些建议:
1. 如果不确定, 就自己测试。
2. 尽量使用基本类型的流 IntStream, LongStream, DoubleStream
3. 有些操作使用并发流的性能会比顺序流的性能更差,比如limit,findFirst,依赖元素顺序的操作在并发流中是极其消耗性能的。findAny的性能就会好很多,应为不依赖顺序。
4. 考虑流中计算的性能(Q)和操作的性能(N)的对比, Q表示单个处理所需的时间,N表示需要处理的数量,如果Q的值越大, 使用并发流的性能就会越高。
5. 数据量不大时使用并发流,性能得不到提升。
6. 考虑数据结构:并发流需要对数据进行分解,不同的数据结构被分解的性能时不一样的。
**流的数据源和可分解性**
![](https://img.kancloud.cn/91/76/9176b3e8b406ccff7cfbd387d17d26c1_939x247.png)
**流的特性以及中间操作对流的修改都会对数据对分解性能造成影响。 比如固定大小的流在任务分解的时候就可以平均分配,但是如果有filter操作,那么流就不能预先知道在这个操作后还会剩余多少元素。**
**考虑终端操作的性能:如果终端操作在合并并发流的计算结果时的性能消耗太大,那么使用并发流提升的性能就会得不偿失。**
- 序
- 快速开始
- 环境要求
- 环境准备
- 工程导入
- 工程运行
- 技术基础
- Java8
- Lambda
- Lambda 受检异常处理
- Stream 简介
- Stream API 一览
- Stream API(上)
- Stream API(下)
- Optional 干掉空指针
- 函数式接口
- 新的日期 API
- Lombok
- SpringMVC
- Swagger
- Mybaties
- Mybaties-plus
- 开发初探
- 新建微服务工程
- 第一个API
- API鉴权
- API响应结果
- Redis 缓存
- 第一个CRUD
- 建表
- 建Entity
- 建Service和Mapper
- 新增API
- 修改API
- 删除API
- 查询API
- 单条查询
- 多条查询
- 分页
- 微服务远程调用
- 声明式服务调用Feign
- 熔断机制 Hystrix
- 开发进阶