目录
一、stream流的分类
二、stream流的用法
1、准备工作
2.流的构造
3.方法测试 (测试方法的用法)
1.filter方法
2.map
3.flatMap
4.distinct()
5.sorted()
6.peek()
7.limit()
8.skip()
9.foreach()
10.forEachOrdered()
11.toArray()
12.reduce()
13.collect()
14.anyMatch()、allMatch()、noneMatch()
15.findFirst()、findAny()
16.stream流的一些操作(待考证)
三、Optional类
分为串行流和并行流。
使用parallelStream方法可以得到一个并行流,并行流底层使用的是forkjoin框架,对于一些计算量比较大的任务,使用并行流可能极大的提升效率。但是使用并行流必须保证每个元素都独立不受影响。每一个元素的功能(function)在做什么及它是否适合运行在并行代码中。当方法是调用一些同步方法,并行流可能会在同步方法上等待,进而导致并行流的性能并没有想象中高。(所以要有选择的使用)。
串行流就是按照顺序执行的,平常最多使用的就是这个。
其中Stream有一个源,0个或者多个中间操作,以及一个终止操作。Stream只有遇到终止操作,它的源才开始执行遍历操作,而且只会进行一次遍历,而不是每个操作都执行一次遍历。
java中的Stream带有lazy执行特征,在整个操作过程中, 只有遇到terminate操作函数,才会触发stream的整体运算。即,如果没有terminate动作,中间不论做什么, 都不会执行。map和peek都属于中间操作,只有执行完终止操作才会对原来的对象进行改变。
常见的操作可以归类如下:
Intermediate 操作(中间操作)
Terminal 操作(终止操作)
Short-circuiting 操作(短路操作)当操作一个无限大的Stream,而又希望在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件。
借鉴stream的分类(上图借鉴的连接,支持原创)
其中limit是一个 short-circuiting stateful intermediate operation (也是一个短路操作)。
这样理解中间操作和终止操作。一个中间操作或者多个中间操作,其返回的结果对象还是一个stream流,但是一旦使用了终止操作,那么返回的就不在是一个stream流,就会返回对应的终止操作的对象。其中属于终止操作的短路操作也是会返回对应的对象,而不是一个流。
(1)实体类 Admin
/**
*
* 管理员信息表
*
*
* @author zwh
* @since 2021-09-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("lib_admin")
public class Admin implements Serializable {
/**
* 管理员id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 密码
*/
private String password;
/**
* 加密盐
*/
private String salt;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
/**
* 是否删除 0 否 false ,1 是 true
*/
private Boolean isDeleted;
}
(2)数据库测试的数据
以上面的数据为基准去测试对应的stream的方法。
一般有三个方法。
1.直接赋值
2.数组构造
3.集合构造
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List list = Arrays.asList(strArray);
stream = list.stream();
(List
通过stream借助lamda表达式对集合collection进行操作(筛选、排序、聚合等),这里采用集合的方法去构造stream流
filter方法是过滤方法,是留下符合条件的数据,形成一个新的stream流。
查出所有被删除的人。
@Override
public List init() {
/*
学习stream流的用法
*/
List adminList = this.baseMapper.selectList(null);
List list = adminList.stream().filter(Admin::getIsDeleted).collect(Collectors.toList());
return list ;
//return this.baseMapper.selectList(Wrappers.lambdaQuery().eq(Admin::getIsDeleted,true));
}
上面可以看出filter的用法是留下符合filter(),括号里面的条件的数据,这里就是符合isdeletde = true ;把符合条件的保存下来,不符合条件的直接过滤。
当然也可以直接用一条sql查询,但是这里是测试filter的用法。
当然filter是一个中间操作,可以继续连接中间操作,知道遇到一个终止操作,这个stream流就停止了。
Returns a stream consisting of the results of applying the given function to the elements of this stream.
返回一个被函数处理后的流。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
使用map()的重要特性就是在()里面设计函数,处理数据。相当于一个迭代器,迭代的处理每一个数据。
List list = adminList.stream().filter(Admin::getIsDeleted).collect(Collectors.toList());
List boleanList = list.stream().map(Admin::getIsDeleted).collect(Collectors.toList());
看到map()和filter()的区别了吗?
同样的操作,filter()返回的是对象,而map()返回的仅仅只是admin的一个字段。
如果想要得到对应filter()一样的数据,返回的就是对应的对象。就是自己筛选符合条件的对象,并把符合条件的对象给返回给stream流,从而形成一个新的stream
List list1 = list.stream().map(i -> {
if (i.getIsDeleted()) {
return i;
} else {
return null;
}
}).collect(Collectors.toList());
或者这么写 Listlist1 = list.stream().map(i -> i.getIsDeleted() ? i : null).collect(Collectors.toList());
注意,map()的i代表的事list的每一个元素。
这其中map()可以干很多事情,毕竟是一个新的迭代器,基本上好多业务都是用map去完成的。
为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong。其实很好理解,如果想将原Stream中的数据类型,转换为double,int或者是long是可以调用相对应的方法。
这三个方法是取出 Admin对象中对应类型的数据,然后形成一个新的流,对于这个流可以进行一些sum(),count(),average()。。。。。等等操作。
比如说
long count1 = adminList.stream().mapToLong(Admin::getId).sum();
这就是取出id字段,然后出所有id的和。
当然也可以把数据取出来作为数组。
long[] array = adminList.stream().mapToLong(Admin::getId).toArray();
for (Long count1:array) {
System.out.println(count1);
}
但是这个不如直接
Listcollect = adminList.stream().map(Admin::getId).collect(Collectors.toList());
所以这三个方法一般都是取出对应的数据进行求和,求平均数等等,一般是数据处理。
long sum = adminList.stream().mapToLong(Admin::getId).sum();
long count = adminList.stream().mapToLong(Admin::getId).count();
OptionalDouble average = adminList.stream().mapToLong(Admin::getId).average();
OptionalLong min = adminList.stream().mapToLong(Admin::getId).min();
OptionalLong max = adminList.stream().mapToLong(Admin::getId).max();
System.out.println("求和"+": "+sum);
System.out.println("求数量"+": "+count);
System.out.println("求平均数"+": "+average);
System.out.println("最小"+": "+min);
System.out.println("最大"+": "+max);
结果
这其中后面三个max(),min(),average() 是Optional 对象,(这个也是java8的新的特性,是显式声明nullexcpetion异常,后面再详细写)
一般改为这样就会变成正常的取值了
System.out.println("求平均数"+": "+average.getAsDouble()); System.out.println("最小"+": "+min.getAsLong()); System.out.println("最大"+": "+max.getAsLong());
Optional对象一般用ispresent()判断之后再用get进行取值。
借鉴对flatMap的jieshao (下图借鉴的连接,支持原创)
flatMap常用作对字符的处理。
同理还有flatMapToInt、flatMapToLong、flatMapToDouble 都类似map的对应的类型。当然要清楚flatMap和map的区别。
去重,因为list数组是有序可重复的数组,set是无序不可重复的数组。
是一个 stateful intermediate operation ,是一个中间操作,对数据进行去重处理。
首先给数据库加几个重复的名字
因为distinct()没有参数,默认是对对象的所有字段进行去重。
List list = adminList.stream().map(Admin::getName).distinct().collect(toList());
list.forEach(System.out::println);
由此可见确实去重了。
排序,有两个方法,一个是有参数的,一个是没参数的。
1、sorted()
默认使用自然序排序, 其中的元素必须实现Comparable
接口
2、sorted(Comparator super T> comparator)
:我们可以使用lambada 来创建一个Comparator
实例。可以按照升序或着降序来排序元素。
System.out.println("========================降序");
List adminList = this.baseMapper.selectList(null);
adminList.stream().sorted(Comparator.comparing(Admin::getId).reversed()).forEach(System.out::println);
System.out.println("============= 升序");
adminList.stream().sorted(Comparator.comparing(Admin::getId)).forEach(System.out::println);
System.out.println("============= 升序");
adminList.stream().sorted(new Comparator() {
@Override
public int compare( Admin o1, Admin o2) {
return o1.getId().compareTo(o2.getId());
}
}).forEach(System.out::println);
上面就是 对应按照某个字段进行升序和降序
如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
List collect = adminList.stream().peek(o ->{
if(o.getIsDeleted()){
o.setName("测试测试");
}else{
o.setName("哦豁");
}
} ).collect(toList());
和map不同不需要返回值return,其余和map差不多。
但是官方文档写的,这个方法用于调试。。。。嗯。。嗯。。尽量不要用把。
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
如同单词意思,限制,截取
List collect = adminList.stream().limit(1).collect(toList());
取出集合的第一个元素。
和单词的意思一样,跳过。跳过指定数字的数据,留下剩下的数据。
List collect1 = adminList.stream().skip(12).collect(toList());
就是循环遍历和普通的foreach用法一样。是一个终止操作
adminList.stream().limit(2).forEach(System.out::println);
顺序执行,严格按照顺序执行
而forerach是并行执行,不一定按照顺序打印出来,但是效率快
System.out.println("==================================================");
adminList.stream().limit(4).parallel().forEach(System.out::println);
System.out.println("=====================这里是分割线=========================");
adminList.stream().limit(4).parallel().forEachOrdered(System.out::println);
如果不使用parallel()的话,两个一样都会按照顺序执行。所以一般都是直接使用foreach
返回一个object类的数组,所以也可以直接把对象作为数组
Object[] array = adminList.stream().map(Admin::getId).toArray();
//Object[] objects = adminList.stream().toArray();
累计(较为复杂),这里是其他人怎么写的,参考一下
这里是链接(借鉴的连接,支持原创)可以自己搜一下具体是怎么实现的。
可以收集流中的数据到【集合】或者【数组】中去。
常见的toList()
这里是链接 (借鉴的连接,支持原创)
一般这种复杂的分组求和等等操作都是在数据库就分好的,除非有特殊需求。
一些分组,分区等操作一般返回的就是map,map用的比较少,一般用的就是list
Map> collect2 = adminList.stream().collect(Collectors.partitioningBy(Admin::getIsDeleted));
按照是否删除进行分区。(这个真不错)
{
"code": 200,
"message": "操作成功",
"data": {
"false": [
{
"id": 1,
"name": "超级管理员",
"password": "de760f9ca0761a2ac076698791498dc7",
"salt": "lib0",
"createTime": "2021-09-01T11:50:26",
"updateTime": "2021-09-01T11:50:26",
"isDeleted": false
},
{
"id": 2,
"name": "赵云",
"password": "aff82c1e6486ac0fc9f2128991e9bf87",
"salt": "lroT",
"createTime": "2021-09-01T14:48:25",
"updateTime": "2021-09-01T14:48:25",
"isDeleted": false
},
{
"id": 4,
"name": "诸葛亮",
"password": "f0b87c5a974f255954ea6b6ad1ed4400",
"salt": "Gz1W",
"createTime": "2021-09-01T15:00:18",
"updateTime": "2021-09-01T15:00:18",
"isDeleted": false
},
{
"id": 5,
"name": "关羽",
"password": "c301e64419486a2f05e927a23a69b1d4",
"salt": "Cmn7",
"createTime": "2021-09-13T14:21:41",
"updateTime": "2021-09-13T14:21:41",
"isDeleted": false
},
{
"id": 6,
"name": "刘备",
"password": "33addba7c0fa9f1a4e1665f14d6ec5b9",
"salt": "rlTD",
"createTime": "2021-10-18T14:04:42",
"updateTime": "2021-10-18T14:04:42",
"isDeleted": false
},
{
"id": 8,
"name": "上官婉儿",
"password": "bb29c7caa87cd6936a1e995dd54307bc",
"salt": "Zm54",
"createTime": "2021-10-18T14:05:23",
"updateTime": "2021-10-18T14:05:23",
"isDeleted": false
},
{
"id": 9,
"name": "克莱汤普森",
"password": "47ae31b05ec8e0248116c903fbf71af0",
"salt": "761X",
"createTime": "2021-10-18T14:05:38",
"updateTime": "2021-10-18T14:05:38",
"isDeleted": false
},
{
"id": 1634546611531,
"name": "库里",
"password": "098672477bc4429a03796ec533a36c02",
"salt": "GkP5",
"createTime": "2021-10-18T16:43:32",
"updateTime": "2021-10-18T16:43:32",
"isDeleted": false
},
{
"id": 1634547212517,
"name": "库里",
"password": "f55b377b4e4d1c8b10ca4f52921698ed",
"salt": "SvZW",
"createTime": "2021-10-18T16:53:33",
"updateTime": "2021-10-18T16:53:33",
"isDeleted": false
},
{
"id": 1634547214180,
"name": "库里",
"password": "bae8b41d9464d610d99da3aca0a491ca",
"salt": "Rg0Z",
"createTime": "2021-10-18T16:53:34",
"updateTime": "2021-10-18T16:53:34",
"isDeleted": false
},
{
"id": 1634547218177,
"name": "库里",
"password": "8f9b76f6b201ccd98bfff6796f70c772",
"salt": "4HVT",
"createTime": "2021-10-18T16:53:38",
"updateTime": "2021-10-18T16:53:38",
"isDeleted": false
}
],
"true": [
{
"id": 3,
"name": "张飞",
"password": "3979872148dabd65d33bb05edcbd2bf9",
"salt": "3M97",
"createTime": "2021-09-01T14:50:41",
"updateTime": "2021-09-01T14:50:41",
"isDeleted": true
},
{
"id": 7,
"name": "曹操",
"password": "825d128d9e7ff2fca7eb0992ef14f520",
"salt": "E5k4",
"createTime": "2021-10-18T14:04:59",
"updateTime": "2021-10-18T14:04:59",
"isDeleted": true
},
{
"id": 10,
"name": "库里",
"password": "533bf258d19ed412f3a55714394d87db",
"salt": "4SC9",
"createTime": "2021-10-18T14:05:49",
"updateTime": "2021-10-18T14:05:49",
"isDeleted": true
}
]
}
}
Collectors.joining() 会根据指定的连接符,将所有元素连接成一个字符串。
//无参数--等价于 joining("");
joining()
//一个参数
joining(CharSequence delimiter)
//三个参数(中间连接+前缀+后缀)
joining(CharSequence delimiter, CharSequence prefix,CharSequence suffix)
String collect3 = adminList.stream().map(Admin::getName).collect(joining());
System.out.println(collect3);
System.out.println("=====================这里是分割线=========================");
System.out.println(adminList.stream().map(Admin::getName).collect(Collectors.joining(",")));
System.out.println("=====================这里是分割线=========================");
System.out.println(adminList.stream().map(Admin::getName).collect(Collectors.joining(",","-_-","^__^")));
anyMatch(Predicate p) 传入一个断言型函数,对流中所有的元素进行判断,只要有一个满足条件就返回true,都不满足返回false。
allMatch(Predicate p) 传入一个断言型函数, 对流中所有的元素进行判断,如果都满足返回true,否则返回false。
nonematch(Predicate p)传入一个断言型函数, 对流中所有的元素进行判断,全都不满足才会返回true。
findFirst() 寻找第一个数据
findAny() 找到数据返回
findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。
两个都是终止操作。
concat 连接两个流 ,of创建流数据,
empty创建一个有序的空的stream流,
generate 生成无限制的流
见下一篇文章
可以参考这边文章
这是链接 (支持原创)