# java8中的Stream API
参考这篇文章: [http://ifeve.com/stream/](http://ifeve.com/stream/),里面的图片真的非常形象
## 1.Stream通用语法
![](https://box.kancloud.cn/8fd1da5ae07e8dde7607282a835a2255_733x186.jpg)
## 2.创建Stream
``` java
//1.Stream.of生成流
Stream stream1 = Stream.of(1, 2, 3, 4);
//2.Arrays.asList
Stream stream2 = Arrays.asList(1, 2, 3, 4).stream();
//3.Arrays.stream
Stream stream3 = Arrays.stream(new String[]{"1", "2", "3", "4"});
//4.Stream.generate 没有入参,会生成一个无限长度的流,相当于"无中生有",经常配合limit来使用
Stream stream4 = Stream.generate(() -> new Random().nextInt(5));
//5.Stream.iterate 根据传入参数迭代生成无限长度流,经常配合limit来使用
Stream stream5 = Stream.iterate(1, x -> x + 1);
```
## 3.转换Stream
- **map**: 数据类型转换
![](https://box.kancloud.cn/45bfb0b113eb909a45552d53ea632c3e_403x202.jpg)
``` java
List list = Stream.of(1, null, 2, null).map(num -> num == null ? 0 : num).collect(Collectors.toList());
System.out.println(list);//[1, 0, 2, 0]
```
- **filter**:过滤掉不符合条件的元素
![](https://box.kancloud.cn/7e4f2635a9cc78b0be962f3fed2367ea_403x206.jpg)
``` java
List list = Stream.of(1, null, 2, null).filter(num -> num != null).collect(Collectors.toList());
System.out.println(list); //[1, 2]
```
- **distinct**: 元素去重
![](https://box.kancloud.cn/7ab402185cf73cb7de61df2ce13fd290_403x205.jpg)
``` java
List list = Stream.of(1, 1, 2, 2, 3, 4).distinct().collect(Collectors.toList());
System.out.println(list); //[1, 2, 3, 4]
```
- **peek**: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数
![](https://box.kancloud.cn/0ed60dee09675e7632898a067c11ebce_403x212.jpg)
以前一直不懂peek存在的意义在哪里,后来看api里面有段话就明白了:
``` java
//This method exists mainly to support debugging,
// where you want to see the elements as they flow past a certain point in a pipeline:
Stream.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
```
```
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
```
简而言之,peek主要用来调试。
需要注意的是,如果没有聚合操作,peek是不会触发的,比如下面这段就不会有任何输出
``` java
Stream.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e));
```
- **skip**: 丢弃Stream前N个元素
![](https://box.kancloud.cn/92d7a8090edc21fe2efb06587410fd37_403x205.jpg)
``` java
List list = Stream.of(1, 2, 3, 4).skip(2).collect(Collectors.toList());
System.out.println(list);//[3, 4]
```
- **limit**: 对Stream进行截断操作, 获取前N个元素
![](https://box.kancloud.cn/34b157bfe63221fe7bc78c10605894db_403x205.jpg)
``` java
List list = Stream.of(1, 2, 3, 4).limit(2).collect(Collectors.toList());
System.out.println(list);//[1, 2]
```
- **flatMap**: 将Stream里面的元素进行数据整合,最终这些元素会放到一条新的数据流中
![](https://box.kancloud.cn/6d61a677e30e8092284c64c0814a7e2f_1224x808.jpg)
``` java
public static void main(String... args) throws InterruptedException {
List list1 = Arrays.asList("123", "234", "345");
List list2 = Arrays.asList("abc", "bcd", "cde");
List> list = Arrays.asList(list1, list2);
List collect = list.stream().flatMap(a -> a.stream()).collect(Collectors.toList());
System.out.println(collect);//[123, 234, 345, abc, bcd, cde]
}
```
## 4.聚合
``` java
public static void main(String... args) throws InterruptedException {
//数据初始化
List list = Arrays.asList(new User("张三", "男", 15), new User("李四", "女", 20), new User("王五", "男", 15));
//1.Collectors.toList() 重新返回列表
List list1 = list.stream().map(User::getName).collect(Collectors.toList());
//2.Collectors.groupingBy() 根据指定字段分组
Map> userMap = list.stream().collect(Collectors.groupingBy(User::getSex));
//3.选中两个字段生成map
Map nameSexMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex));
//4.Collectors.joining 将对象拼成字符串
String nameStr = list.stream().map(User::getName).collect(Collectors.joining(","));
//5.Collectors.toSet 转成set
Set nameSet = list.stream().map(User::getName).collect(Collectors.toSet());
//6.Collectors.partitioningBy 根据条件将对象分词两个组
Map> sexMap = list.stream().collect(Collectors.partitioningBy(user -> "男".equals(user.getSex())));
//7.count() 数量统计
long count = list.stream().count();
//8.Collectors.reducing() reduce操作,给定初始值,两两计算,得到的结果作为后面的初始值
Integer totalAge = list.stream().map(User::getAge).collect(Collectors.reducing(0, (subtotal, age) -> (subtotal + age)));
//9.Collectors.summingInt 数据汇总
Integer sumAge = list.stream().collect(Collectors.summingInt(User::getAge));
//10.Collectors.mapping 配合groupingBy使用,可以对groupingBy生成的list再进行一次聚合
Map> collect = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.mapping(User::getName, Collectors.toSet())));
//11.max 获取最大值
User user = list.stream().max(Comparator.comparingInt(User::getAge)).get();
}
}
class User {
private String name;
private String sex;
private int age;
public User(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
//getter && setter
}
```
## 5.demo
``` java
class User {
private Integer age;
private String name;
private Double salary;
private String sex;
public User(String name, Integer age, Double salary,String sex) {
this.name = name;
this.age = age;
this.salary = salary;
this.sex = sex;
}
// getter setter
}
List userList = Arrays.asList(
new User("张三", 12, 6000d, "男"),
new User("李四", 28, 12000d, "男"),
new User("王五", 8, 4500d, "男"),
new User("阿西吧", null, 3000d, "女"));
```
### 1.从对象列表提取某个字段
``` java
List nameList = userList.stream()
.map(User::getName)
.collect(Collectors.toList());
System.out.println(nameList);
```
```
[张三, 李四, 王五, 阿西吧]
```
### 2.从对象列表提取某个字段,并且用分隔符连接
``` java
String nameStr = userList.stream()
.map(User::getName)
.collect(Collectors.joining(","));
System.out.println(nameStr);
```
```
张三,李四,王五,阿西吧
```
### 3.汇总某个字段
``` java
Double sumSalary = userList.stream()
.filter(user -> user.getSalary() != null)
.collect(Collectors.summingDouble(User::getSalary));
System.out.println(sumSalary);
```
```
25500.0
```
### 4.按照某个字段分组
``` java
Map> map = userList.stream().collect(Collectors.groupingBy(User::getSex));
System.out.println(JSONObject.toJSONString(map));
```
```
{
"女": [
{
"name": "阿西吧",
"salary": 3000.0,
"sex": "女"
}
],
"男": [
{
"age": 12,
"name": "张三",
"salary": 6000.0,
"sex": "男"
},
{
"age": 28,
"name": "李四",
"salary": 12000.0,
"sex": "男"
},
{
"age": 8,
"name": "王五",
"salary": 4500.0,
"sex": "男"
}
]
}
```
### 5.分组汇总
``` java
Map sexSalaryMap = userList.stream()
.collect(Collectors.groupingBy(User::getSex, Collectors.summingDouble(User::getSalary)));
System.out.println(JSONObject.toJSONString(sexSalaryMap));
```
```
{"女":3000.0,"男":22500.0}
```
### 6.统计list中数据重复出现的次数
``` java
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 2, 3, 5, 1, 1, 1, 7, 2);
Map countMap = list.stream().collect(Collectors.groupingBy(num -> num, Collectors.counting()));
for (Map.Entry entry : countMap.entrySet()) {
System.out.println("数字 " + entry.getKey() + " 出现了 " + entry.getValue() + " 次");
}
```
```
数字 1 出现了 4 次
数字 2 出现了 3 次
数字 3 出现了 2 次
数字 4 出现了 1 次
数字 5 出现了 2 次
数字 6 出现了 1 次
数字 7 出现了 1 次
```
## 参考文档:
[http://ifeve.com/stream/](http://ifeve.com/stream/)
[https://www.java67.com/2016/03/how-to-use-flatmap-in-java-8-stream.html](https://www.java67.com/2016/03/how-to-use-flatmap-in-java-8-stream.html)
[https://www.baeldung.com/java-stream-reduce](https://www.baeldung.com/java-stream-reduce)