springboot3(二、StreamAPI)

文章目录

  • 一、StreamAPI
    • 1.介绍
    • 2.代码示例
  • 二、基本用法
    • 1.创建流
    • 2.流并发
    • 3.流并发问题
  • 三、流方法
    • 1.中间操作
    • 2.终止操作

一、StreamAPI

StreamAPI这种函数式编程是声明式编程,声明式编程是一种编程范式,它着重于描述问题的"是什么",而不是"如何做"。在声明式编程中,我们更关注问题的定义和规范,而不需要显式地指定每个步骤的实现细节。

1.介绍

Stream Pipeline:流管道、流水线
Intermediate Operations:中间提作
Terminal Operation:终止操作

Stream所有数据和操作被组合成流管道,流管道组成:

  • 一个数据源(可以是一个数组、集合、生成器函数、I/O管道)零或多个中间操作(将一个流变形成另一个流)一个终止操作(产生最终结果)
  • 流是惰性的,只有在启动最终操作时才会对源数据进行计算,而且只在需要时才会消耗源元素;

2.代码示例

获取列表中最大的偶数,示例如下:

public class StreamApi {
    public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 获取最大偶数
        Integer max = 0;
        for (Integer i : list) {
            if (i % 2 != 0) {
                continue;
            }
            max = i >= max ? i : max;
        }
        System.out.println(max);

        /*****************************StreamAPI******************************************/
        list.stream().filter(integer -> integer % 2 == 0) //  intermediate operation
                .max(Integer::compareTo) //  terminal operation
                .ifPresent(System.out::println);
    }

}

输出:

10
10

在这里插入图片描述
在这里插入图片描述
总结流的三大部分:

  1. 流的数据
  2. n个中间操作
  3. 1一个终止操作

二、基本用法

1.创建流

public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Stream<Integer> stream1 = Stream.of(1,2,3);
        Stream<Integer> concatStream = Stream.concat(stream1, Stream.of(2,3,4));
        System.out.println(concatStream.toList()); // 输出:[1, 2, 3, 2, 3, 4]
        Set<Integer> set = Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        set.stream().min(Integer::compareTo).ifPresent(System.out::println);
        list.stream().min(Integer::compareTo).ifPresent(System.out::println);
}

创建流的方法有很多,代码中举了几个例子:

  • Stream.of
  • Stream.concat
  • 集合的初始化

2.流并发

下面思考两个问题:

  1. 流是并发的吗?
  2. 与for循环有哪些区别?
    public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println("主线程:"+Thread.currentThread());
        list.stream().filter(integer -> {
                    System.out.println("stream线程"+Thread.currentThread());
                    return integer % 2 == 0;
                }) //  intermediate operation
                .max(Integer::compareTo) //  terminal operation
                .ifPresent(System.out::println);
    }

输出:

主线程:Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
10

实践证明,目前的写法是没有并发的,还是在主线程中完成的。
与for循环并无区别。

但是目前我们数据量非常大,需要开并发,那么就可以使用parallel解决

    public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println("主线程:"+Thread.currentThread());
        list.stream().parallel().filter(integer -> {
                    System.out.println("stream线程"+Thread.currentThread());
                    return integer % 2 == 0;
                }) //  intermediate operation
                .max(Integer::compareTo) //  terminal operation
                .ifPresent(System.out::println);
    }

输出:

主线程:Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[main,5,main]
stream线程Thread[ForkJoinPool.commonPool-worker-1,5,main]
stream线程Thread[main,5,main]
stream线程Thread[ForkJoinPool.commonPool-worker-3,5,main]
stream线程Thread[ForkJoinPool.commonPool-worker-2,5,main]
10

注意事项:
开并发,尽可能在函数内部操作数据,避免出现安全问题。

3.流并发问题

 private static int count = 0;

    public static void main(String[] args) {
        /*****************************StreamAPI*********************************/
        System.out.println("主线程:"+Thread.currentThread());
        IntStream.range(0,100).parallel() 
                .forEach(integer -> {//  terminal operation
                    count++;
                });
        System.out.println("数据变化:"+count);
   }

看下这段代码的,由于我们写了parallel开了并发,操作的变量又是在函数外部定义的,所以是会出现安全问题的。
输出的结果可能是小于等于100的任意数字吧。
这时候一定要并发的情况下,那修改方式就是加锁,或者是换成线程安全的类型进行操作(AtomicInteger)。
代码如下:

 private static int count = 0;

    public static void main(String[] args) {
        /*****************************StreamAPI*********************************/
        System.out.println("主线程:"+Thread.currentThread());
        IntStream.range(0,100).parallel() 
                .forEach(integer -> {//  terminal operation
                    synchronized (Object.class)
                    {count++;}
                });
        System.out.println("数据变化:"+count);
   }

    public static void main(String[] args) {
        /*****************************StreamAPI******************************************/
        System.out.println("主线程:"+Thread.currentThread());
        AtomicInteger count = new AtomicInteger();
        IntStream.range(0,100).parallel() //  terminal operation
                .forEach(integer -> {
                        count.getAndIncrement();
                });
        System.out.println("数据变化:"+count);
  }

三、流方法

流的过程:每一个数据 流经 所有管道之后才会再进行下一个数据的流操作

1.中间操作

  • parallel:并发
  • filter:筛选:遍历流中的每一个元素,取出符合条件的数据
  • map:一对一处理数据,返回数据
  • peek:打印
  • sorted:排序
  • distinct:去重
  • limit:截取流中前 N 个元素
    /**
     * 中间操作测试
     *
     * @param args
     */
    public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 8,9, 10);
        list.stream().limit(9)
        		.filter(integer -> {
                    System.out.println("filter输出:"+integer);
                    return integer > 4;
                })
                .peek(i -> System.out.println("peek输出:" + i))
                .map(integer -> {
                    System.out.println("map输出:"+integer);
                    return integer;
                })
                .sorted(Integer::compareTo)
                .distinct()
                .forEach(integer -> {
                    System.out.println("foreach输出:" + integer);
                });
    }

输出:

filter输出:1
filter输出:2
filter输出:3
filter输出:4
filter输出:5
peek输出:5
map输出:5
filter输出:6
peek输出:6
map输出:6
filter输出:7
peek输出:7
map输出:7
filter输出:8
peek输出:8
map输出:8
filter输出:8
peek输出:8
map输出:8
foreach输出:5
foreach输出:6
foreach输出:7
foreach输出:8

从输出中可以看到每个数据是走完整个中间流之后才会再进行下一个数据的中间操作的。

  • flatMap:一对多处理数据,返回数据
  • skip:跳过流中前 N 个元素
  • takeWhile:筛选:当满足条件,拿到这个元素,结束流循环
  /**
     * 中间操作测试
     * - flatMap:一对多处理数据,返回数据
     * - skip:跳过流中前 N 个元素
     * - takeWhile:筛选:当满足条件,拿到这个元素,结束流循环
     *
     * @param args
     */
    public static void main(String[] args) {
        List<Map<String, Object>> list = new ArrayList<>();

        list = IntStream.range(0, 10)
                .mapToObj(integer -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("name", "Bob"+integer);
                    return map;
                }).collect(Collectors.toList());
        System.out.println(list);
        //flatMap 一对多输出
        List<String> strList = list.stream()
                .skip(5)
                .flatMap(m -> Arrays.stream(m.get("name").toString().split("Bob")))
                .filter(s -> !s.isEmpty())
                .distinct()
                .toList();
        System.out.println(strList);
        List<String> strList1 = strList.stream().takeWhile(str -> 8 >= Integer.parseInt(str)).collect(Collectors.toList());
        System.out.println(strList1);

    }

输出:

[{name=Bob0}, {name=Bob1}, {name=Bob2}, {name=Bob3}, {name=Bob4}, {name=Bob5}, {name=Bob6}, {name=Bob7}, {name=Bob8}, {name=Bob9}]
[5, 6, 7, 8, 9]
[5, 6, 7, 8]

2.终止操作

  • foreach:对流中的每个元素执行指定操作,没有返回值。
  • max:返回最大值
  • count:返回数据总条数
  • anyMatch:判断流中是否存在满足条件的元素,如果存在则返回 true,否则返回 false。
  • collect:将流中的元素收集到一个容器中。例如,整合一个分组后的数据到容器中,collect(Collectors.groupingBy(s -> s))
  • reduce:通过累加器和初始值对流中的元素进行归约操作,返回归约后的结果。例如,可以使用 reduce(0, (a, b) -> a + b) 对整型流求和。
  • findFirst:返回流中的第一个元素(如果存在),否则返回一个空的 Optional 对象。
  • findAny:返回流中的任意一个元素(如果存在),否则返回一个空的 Optional 对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/310371.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解.NET框架中的CLR(公共语言运行时)

深入理解.NET框架中的CLR&#xff08;公共语言运行时&#xff09; 引言 .NET框架中的CLR&#xff08;公共语言运行时&#xff09;是.NET应用程序运行的核心。本文将继续探索CLR的核心功能&#xff0c;并详细介绍.NET程序启动时是如何自动加载关键的库和服务来提供这些功能的。…

初识QT。

文章目录 前言一、QWidget1、了解内容main文件中的基本内容。.pro项目文件的内容。mywidget.h文件内容。命名规范和快捷键Qt助手 2、button按钮3、对象树4、信号和槽5、自定义信号和槽函数拓展 6、Lambda表达式7、练习 二、QMainWindow1、菜单栏和菜单项2、工具栏3、状态栏4、铆…

适用于安防 音响 车载等产品中中的音频接口选型分析

在人工智能兴起之后&#xff0c;安防市场就成为了其全球最大的市场&#xff0c;也是成功落地的最主要场景之一。对于安防应用而言&#xff0c;智慧摄像头、智慧交通、智慧城市等概念的不断涌现&#xff0c;对于芯片产业催生出海量需求。今天&#xff0c;我将为大家梳理GLOBALCH…

启用Hyper-V的三种方法,总有一种适合你

想在Windows 10计算机上的虚拟机中安装并运行Linux或更早版本的Windows操作系统吗&#xff1f;你将很高兴知道&#xff0c;你不需要第三方虚拟化软件&#xff08;如VirtualBox&#xff09;来在Windows 10上安装和运行Linux和Windows。 Windows 10中的内置Hyper-V工具允许你创建…

Android Studio 实现飞机大战游戏App

&#x1f345;文章末尾有获取完整项目源码方式&#x1f345; 目录 前言 一、运行演示 二、开发环境 三、完成步骤 步骤 1&#xff1a;创建项目 步骤 2&#xff1a;创建包名 步骤 3&#xff1a;实现启动页 步骤 5&#xff1a;实现用户注册 步骤 6&#xff1a;实现用户登…

逃离支原体又陷入流感 | 揭秘呼吸道感染与肠道菌群的隐秘关联

谷禾健康 成人每年大约会患上两到三次普通感冒&#xff0c;儿童每年可能会患上多达八次。而今年尤为显著&#xff0c;各大医院的儿科还是爆满状态&#xff0c;甚至是一号难求。 肺炎支原体感染还没彻底过去&#xff0c;紧接着流感和其他呼吸道病原体感染又跟来了&#xff0c;医…

ffmpeg写YUV420文件碰到阶梯型横线或者条纹状画面的原因和解决办法

版权声明&#xff1a;本文为CSDN博主「文三~」的原创文章&#xff0c;遵循CC 4.0 BY-SA版权协议&#xff0c;转载请附上原文出处链接及本声明。 原文链接&#xff1a;https://blog.csdn.net/asdasfdgdhh/article/details/112831581 留作备份 阶梯型横线&#xff1a; 条纹状画面…

vue前端开发自学,父子组件之间的数据传递demo

vue前端开发自学,父子组件之间的数据传递demo!下面为大家展示的是&#xff0c;vue开发中常见的&#xff0c;父子级别关系的&#xff0c;数据 传递案例。先给大家看一下&#xff0c;源码&#xff0c;然后讲解一下里面的注意事项。 <template><h3>Parent</h3>…

耐高压达林顿输出光隔离器TLP187(TPL,E(O 功能介绍及其应用

TLP187(TPL,E(O 是一款达林顿晶体管耦合器&#xff0c;它因采用了 SO6封装而实现了外形的小巧化&#xff0c;也可以保证高温运转的可行性 &#xff08;Ta110degG最大值)。TLP187可以取代其前一代产品&#xff0c;即现有的TIP127型号。该产品的的焊盘尺寸与现有的MFSOP时装参考尺…

ajax+axios——统一设置请求头参数——添加请求头入参——基础积累

最近在写后台管理系统&#xff08;我怎么一直都只写管理系统啊啊啊啊啊啊啊&#xff09;&#xff0c;遇到一个需求&#xff0c;就是要在原有系统的基础上&#xff0c;添加一个仓库的切换&#xff0c;并且需要把选中仓库对应的id以请求头参数的形式传递到每一个接口当中。。。 …

启动redis出现Creating Server TCP listening socket 127.0.0.1:6379: bind: No error异常

1.进入redis安装目录&#xff0c;地址栏输入cmd 2.输入命令 redis-server.exe redis.windows.conf redis启动失败 解决&#xff0c;输入命令 #第一步 redis-cli.exe#第二步 shutdown#第三步 exit第四步 redis-server.exe redis.windows.conf 显示以下图标即成功

【论文阅读】Deep Graph Infomax

目录 0、基本信息1、研究动机2、创新点2.1、核心思想&#xff1a;2.2、思想推导&#xff1a; 3、准备3.1、符号3.2、互信息3.3、JS散度3.4、Deep InfoMax方法3.5、判别器&#xff1a;f-GAN估计散度 4、具体实现4.1、局部-全局互信息最大化4.2、理论动机 5、实验设置5.1、直推式…

Linux第25步_在虚拟机中备份“ST官方的TF-A源码”

TF-A是ARM公司提供的&#xff0c;ST公司通过修改它&#xff0c;做了一个自己的TF-A代码。因为在后期开发中&#xff0c;若硬件被改变了&#xff0c;我们需要通过修改"ST官方的TF-A源码"就可以自己的TF-A代码了。为了防止源文件被误改了&#xff0c;我们需要将"S…

Linux安装MongoDB教程

下载MongoDB安装包 1、官网地址; 下载链接 选择版本 下载好了之后上传到服务器开始安装。 解压 解压 mongodb-linux-x86_64-rhel70-4.2.23.tgz 文件&#xff1a; 解压文件必须进入到压缩包所在的目录&#xff1a; cd /usr/local tar -zxvf mongodb-linux-x86_64-rhel70-4…

HDFS读写数据流程、NameNode与DataNode工作机制

文章目录 HDFS 写数据流程HDFS 读数据流程HDFS 节点距离计算HDFS 机架感知HDFS NN和2NN工作机制HDFS FsImage镜像文件HDFS Edits编辑日志HDFS 检查点CheckPoint时间设置HDFS 退役旧数据节点HDFS DataNode多目录配置HDFS DataNode工作机制HDFS 数据完整性HDFS 掉线时限参数设置 …

在线HMAC计算工具

HMAC在线加密 - BTool在线工具软件&#xff0c;为开发者提供方便。HMAC是密钥相关的哈希运算消息认证码&#xff08;Hash-based Message Authentication Code&#xff09;的缩写&#xff0c;由H.Krawezyk&#xff0c;M.Bellare&#xff0c;R.Canetti于1996年提出的一种基于Hash…

NATURE子刊 | IF:9.8,中科院2区水刊,审稿速度快!接收领域广!

【SciencePub学术】本期&#xff0c;小编给大家推荐的是一本影响因子为9.0的中科院2区Nature子刊。其详情如下&#xff1a; 期刊简介 SCIENTIFIC DATA ISSN&#xff1a;2052-4463 E-ISSN&#xff1a;—— IF&#xff08;2022&#xff09;&#xff1a;9.8 自引率&#…

【EI会议征稿通知】第五届机电一体化技术与智能制造国际学术会议(ICMTIM 2024)

第五届机电一体化技术与智能制造国际学术会议&#xff08;ICMTIM 2024&#xff09; 2024 5th International Conference on Mechatronics Technology and Intelligent Manufacturing 第五届机电一体化技术与智能制造国际学术会议&#xff08;ICMTIM 2024&#xff09;将于2024…

单极子天线

当双极子天线的一个臂演变为无限大地平面时就形成了一个单极子天线&#xff0c;依据单极子天线形状的不同可以将单极子划分为不同的种类&#xff0c;例如三角锥形、圆锥形、袖形等&#xff0c;这里只关注普通的垂直接地细直单极子天线。 依据镜像原理&#xff0c;单极子天线模型…

ubuntu 挂载新硬盘

1、检测新硬盘 新增加硬盘&#xff0c;检测硬盘识别情况。 命令检查&#xff1a;sudo fdisk -l 3、格式化磁盘 格式化&#xff1a;sudo mkfs.ext4 /dev/sdb 其中&#xff0c;/dev/sdb是新分区的设备文件名&#xff0c;ext4是要使用的文件系统类型。 4、挂载新分区 sudo mk…