栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java8 的stream流

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java8 的stream流

目录

一、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类


一、stream流的分类

分为串行流和并行流。

使用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流,就会返回对应的终止操作的对象。其中属于终止操作的短路操作也是会返回对应的对象,而不是一个流。

二、stream流的用法 

1、准备工作

(1)实体类 Admin 

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("lib_admin")
public class Admin implements Serializable {


    
    @TableId(type = IdType.AUTO)
    private Long id;

    
    private String name;

    
    private String password;

    
    private String salt;

    
    private LocalDateTime createTime;

    
    private LocalDateTime updateTime;

    
    private Boolean isDeleted;


}

(2)数据库测试的数据

 以上面的数据为基准去测试对应的stream的方法。

2.流的构造

一般有三个方法。

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();

3.方法测试 (测试方法的用法)

(List adminList = this.baseMapper.selectList(null)):默认的adminList为全查表数据

通过stream借助lamda表达式对集合collection进行操作(筛选、排序、聚合等),这里采用集合的方法去构造stream流

1.filter方法

filter方法是过滤方法,是留下符合条件的数据,形成一个新的stream流。

查出所有被删除的人。

    @Override
    public List init() {
        
        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流就停止了。

2.map
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());
 或者这么写 List list1 = 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);
        }

但是这个不如直接

List collect = 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进行取值。

3.flatMap

借鉴对flatMap的jieshao (下图借鉴的连接,支持原创)

 flatMap常用作对字符的处理。

同理还有flatMapToInt、flatMapToLong、flatMapToDouble 都类似map的对应的类型。当然要清楚flatMap和map的区别。

4.distinct() 

去重,因为list数组是有序可重复的数组,set是无序不可重复的数组。

是一个 stateful intermediate operation ,是一个中间操作,对数据进行去重处理。

首先给数据库加几个重复的名字

因为distinct()没有参数,默认是对对象的所有字段进行去重。

        List list = adminList.stream().map(Admin::getName).distinct().collect(toList());
        list.forEach(System.out::println);

 

 由此可见确实去重了。

5.sorted()

排序,有两个方法,一个是有参数的,一个是没参数的。

1、sorted() 默认使用自然序排序, 其中的元素必须实现Comparable 接口
2、sorted(Comparator 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);

上面就是 对应按照某个字段进行升序和降序

6.peek()

如同于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

 7.limit()

如同单词意思,限制,截取

  List collect = adminList.stream().limit(1).collect(toList());

取出集合的第一个元素。

8.skip()

和单词的意思一样,跳过。跳过指定数字的数据,留下剩下的数据。

List collect1 = adminList.stream().skip(12).collect(toList());

 

9.foreach()

就是循环遍历和普通的foreach用法一样。是一个终止操作

adminList.stream().limit(2).forEach(System.out::println);

10.forEachOrdered()

顺序执行,严格按照顺序执行

而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

11.toArray()

返回一个object类的数组,所以也可以直接把对象作为数组

    Object[] array = adminList.stream().map(Admin::getId).toArray();
     //Object[] objects = adminList.stream().toArray();

12.reduce()

累计(较为复杂),这里是其他人怎么写的,参考一下

这里是链接(借鉴的连接,支持原创)可以自己搜一下具体是怎么实现的。

13.collect()

可以收集流中的数据到【集合】或者【数组】中去。

常见的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(",","-_-","^__^")));

14.anyMatch()、allMatch()、noneMatch()

anyMatch(Predicate p) 传入一个断言型函数,对流中所有的元素进行判断,只要有一个满足条件就返回true,都不满足返回false。

allMatch(Predicate p) 传入一个断言型函数, 对流中所有的元素进行判断,如果都满足返回true,否则返回false。

nonematch(Predicate p)传入一个断言型函数, 对流中所有的元素进行判断,全都不满足才会返回true。

15.findFirst()、findAny()

findFirst() 寻找第一个数据

findAny() 找到数据返回

findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。

两个都是终止操作。

16.stream流的一些操作(待考证)
concat 连接两个流 ,of创建流数据,
empty创建一个有序的空的stream流,
generate 生成无限制的流

三、Optional类

见下一篇文章

可以参考这边文章

这是链接 (支持原创)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/341177.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号