什么是流
Java 8 API添加了一个新的抽象称为流Stream,其可以以一种声明式的方式处理数据。它将基础操作链接起来,能完成复杂的数据处理流水线,并提供了透明的并行处理机制。
Stream流的组成
- 数据源
- 中间操作
- 终端操作
一图胜千言,从整体上了解下相关的内容
流与集合的区别:
- 流相对于是时间上的集合,得到的是某一时刻的数据(面向计算)
- 集合相对于是空间上的集合,需要真实的存储(面向存储)
- 流只能遍历一次,集合可以反复操作
- 流是内部迭代,集合是外部迭代
怎么使用流编程
下面将对流编程的操作和构建一一进行演示
创建一个实体类User方便我们使用
class User {
private Integer userId;
private String userName;
private String password;
public User(Integer userId, String userName, String password) {
this.userId = userId;
this.userName = userName;
this.password = password;
}
public Integer getUserId() {
return userId;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
}
创建一个集合,作为我们数据操作的数据
List<User> list = new ArrayList<User>() {
{
add(new User(5,"ckhhh", "123123"));
add(new User(1,"zhangsan", "1232312"));
add(new User(3,"lisi", "000000"));
add(new User(2,"lisi", "4324324"));
add(new User(4,"wangwu", "99999"));
}
};
中间操作
- 无状态的
- filter使用
/**
* filter使用 按照指定条件过滤集合
*/
list.stream().
filter(user -> user.getUserName().equals("lisi")).
forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
打印用户名为lisi的用户的信息,结果如下:
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
- map使用
/**
* map使用 从一种类型转化为另一种类型的集合,user->string
*/
list.stream().
map(user -> user.getUserName()).
forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
使用map将集合分割,只获取所有用户并打印,结果如下:
"ckhhh"
"zhangsan"
"lisi"
"lisi"
"wangwu"
- flatMap操作
/**
* flatMap操作 接收一个元素,返回一个新的流(将对象转换为流操作)
*/
list.stream()
.flatMap(user -> Arrays.stream(user.getUserName().split("")))
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
使用flatMap将集合变为新的流:将名字分割成字符,然后打印,结果如下:
"c"
"k"
"h"
"h"
"h"
"z"
"h"
"a"
"n"
"g"
"s"
"a"
"n"
"l"
"i"
"s"
"i"
"l"
"i"
"s"
"i"
"w"
"a"
"n"
"g"
"w"
"u"
- peek操作
/**
* peek 中间操作 无状态的 操作完后流还可以使用 foreach使用后就不能用了
* peek和foreach交替打印
*/
list.stream()
.peek(user -> System.out.println(user.getUserName()))
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
打印用户名,因为是无状态的,所以会执行完一下peek后执行一下foreach中的打印,结果如下
ckhhh
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
zhangsan
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
lisi
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
lisi
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
wangwu
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
- 有状态的
- sort操作
/**
* sort操作,对数据按照规则排序
* sorted是有状态的 所以会等peek结束之后再操作
* peek和foreach就不会再交替打印了
*/
list.stream()
.peek(user -> System.out.println(user.getUserName()))
.sorted(Comparator.comparing(User::getUserId))
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
排序后打印,是有状态的,所以必须所有peek执行完了后才能执行,根据userId升序排列,结果如下:
ckhhh
zhangsan
lisi
lisi
wangwu
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- distinct操作
/**
* distinct操作 去除重复
*/
list.stream()
.map(user -> user.getUserName())
.distinct()
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
去除用户名中的重复项后打印用户名,结果如下:
"ckhhh"
"zhangsan"
"lisi"
"wangwu"
- skip操作
/**
* skip过滤掉前三条
*/
list.stream()
.sorted(Comparator.comparing(user ->user.getUserId()))
.skip(3)
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
按id升序排序后,跳过前三条,从第四条开始打印,结果如下:
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- limit操作
/**
* limit操作 获取前三条
*/
list.stream()
.sorted(Comparator.comparing(user ->user.getUserId()))
.limit(3)
.forEach(user ->
System.out.println(JSON.toJSONString(user, true)));
按id升序排列后,打印前三条,结果如下:
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
{
"password":"000000",
"userId":3,
"userName":"lisi"
}
终端操作
- 短路的操作
- allMatch操作
/**
* allMatch 使用:是否全部满足终端操作,短路的操作
* 如果发现了不匹配的 就不会继续执行了,peek中打印的数据打不符合的就停止了
*/
boolean b = list.stream()
.peek(user -> System.out.println(user.getUserId()))
.allMatch(user->user.getUserId()> 2);
System.out.println(b);
判断是否所有的用户id都大于2,一旦发现小于等于2的就返回false,结果如下
5
1
false
- anyMatch操作
/**
* anyMatch使用 :只要有满足的,就返回true
* 短路的 找到一个符合的就返回true
*/
boolean c = list.stream()
.peek(user -> System.out.println(user.getUserId()))
.anyMatch(user->user.getUserId()> 2);
System.out.println(c);
判断是否有用户id大于2,一旦发现大于2的就返回true,结果如下
5
true
- noneMatch操作
/**
* noneMatch使用:所有的都没匹配上 返回true
* 短路的,找到一个符合的就返回false
*/
boolean d = list.stream()
.peek(user -> System.out.println(user.getUserId()))
.noneMatch(user->user.getUserId()> 2);
System.out.println(d);
判断是否没有用户id大于2,一旦发现大于2的就返回false,结果如下:
5
false
- findFirst操作
/**
* findFirst:获取第一个对象
* 短路的 获取了就返回
*/
Optional<User> first = list.stream()
.findFirst();
System.out.println(JSON.toJSONString(first.get(), true));
返回第一个对象,结果如下
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- findAny操作
/**
* findAny找到任何一个就返回
* 并行操作时findAny会更快,可能会随机匹配 如果串行一致
*/
Optional<User> first1 = list.stream()
.findAny();
System.out.println(JSON.toJSONString(first1.get(), true));
返回任意一个对象,在串行下和返回第一个对象一样,在并行下就是随机的,结果如下:
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
- 非短路操作
- max操作
/**
* max操作:获取最大值
*/
OptionalDouble max = list.stream()
.mapToDouble(User::getUserId)
.max();
System.out.println(max.getAsDouble());
将用户id列分割出来,将值转为doble,返回最大值,结果如下:
5.0
- min操作
/**
* min操作:获取最小值
*/
OptionalDouble min = list.stream()
.mapToDouble(User::getUserId)
.min();
System.out.println(min.getAsDouble());
将用户id列分割出来,将值转为doble,返回最小值,结果如下:
1.0
- count操作
/**
* count操作:获取元素的个数
*/
long count = list.stream()
.count();
System.out.println(count);
统计元素个数,结果如下:
5
- collect操作
/**
* 集合收集器
*/
List<User> result = list.stream()
.filter(user -> user.getUserId() > 2)
.collect(Collectors.toList());
System.out.println(JSON.toJSONString(result, true));
将id大于2的元素重新写入集合,打印结果如下:
[
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
},
{
"password":"000000",
"userId":3,
"userName":"lisi"
},
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
]
/**
* 分组收集器
*/
Map<String, List<User>> group = list.stream()
.collect(Collectors.groupingBy(
user -> user.userName));
System.out.println(JSON.toJSONString(group, true));
将元素按用户名分组后打印,结果如下:
{
"lisi":[
{
"password":"000000",
"userId":3,
"userName":"lisi"
},
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
],
"ckhhh":[
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
}
],
"zhangsan":[
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
}
],
"wangwu":[
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
]
}
/**
* 分区收集器
* 分为大于2和小于等于2两部分
*/
Map<Boolean, List<User>> partition = list.stream()
.collect(Collectors.partitioningBy(user -> user.getUserId() > 2));
System.out.println(JSON.toJSONString(partition, true));
将元素按id大于2位标准分为两个区后进行打印,结果如下:
{false:[
{
"password":"1232312",
"userId":1,
"userName":"zhangsan"
},
{
"password":"4324324",
"userId":2,
"userName":"lisi"
}
],true:[
{
"password":"123123",
"userId":5,
"userName":"ckhhh"
},
{
"password":"000000",
"userId":3,
"userName":"lisi"
},
{
"password":"99999",
"userId":4,
"userName":"wangwu"
}
]
}
构建流
- 由数值直接构建流
public void steamFromValue() {
Stream stream = Stream.of(1, 2, 3, 4, 5);
stream.forEach(System.out::println);
}
运行结果如下:
1
2
3
4
5
- 由数组构建流
public void streamFromArray() {
int[] num = new int[]{1, 2, 3, 4, 5};
Arrays.stream(num)
.forEach(System.out::println);
}
运行结果如下:
1
2
3
4
5
- 通过文件构建流
public void streamFromFile() throws IOException {
Stream<String> stream = Files.lines(Paths.get("F:/QQ下载/FileServer.java"));
stream.forEach(System.out::println);
}
运行结果如下(部分)
package com.demo.lambda;
import java.io.*;
/**
* 〈文件服务类〉
*
* @author Chkl
* @create 2020/4/20
* @since 1.0.0
*/
public class FileServer {
/**
* 通过url获取本地文件内容
*
* @param url
* @param fileConsumer
*/
public void fileHandler(String url, FileConsumer fileConsumer) throws IOException {
..............
...............
- 通过函数生成流(无限流)
public void streamFromFunction() {
//参数: 初始值,规则(从0开始生成偶数)
Stream<Integer> stream = Stream.iterate(0, n -> n + 2);
stream.limit(100)
.forEach(System.out::println);
//随机生成
Stream<Double> stream2 = Stream.generate(Math::random);
stream2.limit(100)
.forEach(System.out::println);
}
第一种运行结果如下(部分):
0
2
4
6
8
10
12
14
16
18
20
22
24
第二种运行结果如下(部分):
0.28047181698982915
0.3366379505477519
0.6314896823002529
0.4202209521644461
0.8362832087391032
0.7025533309035066
0.49208504805565545
0.18517793464530763
0.37580620856029856
0.8888281509032474
0.8399345540793907
0.6470816912713735
0.9842389606178953
0.5166615554196825
0.035214233629631164
通过以上的学习,可以基本的掌握Stream流常用的一些操作和构建方便了,具体业务需要使用到的方法在使用时再去了解