Java真的不难(五十二)Stream流的深入理解与流式编程

news2025/2/4 8:59:04

Stream流的深入理解与流式编程:

在今年年初小应学长也写过Stream与Lambda的两篇文章,今天先将两者结合起来进行流式编程
在这里插入图片描述


一、关于这两者

Stream与Lambda都是Java8引入的新概念,lambda在Java编程中对于函数式编程的支持,有助于代码的简洁,可以取代大半部分的匿名函数,尤其对于集合的遍历和集合的操作,极大的简化了代码。而Stream是使用函数式编程方式在集合类上进行操作的工具。

小结: 我们可以让Stream流使用lambda表达式来进行流式编程(让代码整洁、美观、高级感)


二、Lambda与Stream的结合使用

1、简单的筛选数据

一个实体类Person:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;
    private String Sex;
}

这里可以使用Lombok的注解或者手动写get/set、构造方法

public class Test {

    private static final List<Person> personList = new ArrayList<>();

    static {
        personList.add(new Person("张三", 20, "男"));
        personList.add(new Person("李四", 25, "男"));
        personList.add(new Person("王五", 30, "男"));
        personList.add(new Person("小美", 18, "女"));
        personList.add(new Person("小红", 25, "女"));
    }

    //正常的for写法:找出年龄小于25岁的人
    public static void test01() {
        for (Person p : personList) {
            if (p.getAge()<25){
                //筛选后添加数据即可
            }
        }
    }
    
    //使用Stream流处理找出年龄小于25岁的人
    public static void test02() {
        List<Person> list = personList.stream().filter(p->p.getAge()<25).collect(Collectors.toList());
    }
}
    

上述代码中首先创建一个Person类,通过构造方法进行初始化并存入列表(模仿从数据库查询返回的数据),test01中用正常的for循环再对比,把符合条件的数据再进行处理。test02则使用stream来处理数据,加上Lambda的写法,最终就一行代码,这也就是流式编程。

多条件的查询

    //年龄小于25岁且为性别为男的人
    public static void test02() {
        List<Person> list = personList.stream()
                .filter(p->p.getAge()<30)
                .filter(p->p.getSex().equals("男"))
                .collect(Collectors.toList());
    }

在stream后面多加filter即可

甚至还可以把条件放到外面进行传参的操作,比如这样:

   //年龄小于25岁且为性别为男的人
    public static void test03(Predicate<? super Person> p) {
        List<Person> list = personList.stream()
                .filter(p)
                .collect(Collectors.toList());
        System.out.println(list);
    }

    public static void main(String[] args) {
        test03(p->p.getAge()<25&&p.getSex().equals("男"));
    }

上述代码在main方法执行后,输出结果为[Person(name=张三, age=20, Sex=男)],没问题


2、分组操作

对上面的列表数据进行按性别分组算平均年龄:
普通写法:

 public static void test04() {
        Map<String, List<Person>> map = new HashMap<>();
        for (Person p : personList) {
            //若当前性别没有分组(List),则new一个List
            List<Person> list = map.computeIfAbsent(p.getSex(), k -> new ArrayList<>());
            list.add(p);
        }
        for (Map.Entry<String, List<Person>> entry : map.entrySet()) {
            int agvAge = 0;
            for (Person person : entry.getValue()) {
                agvAge += person.getAge();
            }
            System.out.printf("平均年龄%s",agvAge/entry.getValue().size());
        }
    }

以往的普通步骤就是先循环List内每一个数据,然后通过get获取性别去判断是否等于男/女,然后存入对应Map,再去遍历这个Map,get到这个对象的年龄,通过累加然后除该Map的长度。

虽然上述代码有逻辑性、顺序,但是代码量多,用了三个for

使用Stream流的写法:

    public static void test05() {
        personList.stream().collect(Collectors.groupingBy(p -> p.getSex(),
                Collectors.averagingInt(p->p.getAge())))
                .forEach((k,v)-> System.out.println(k+":"+v));
    }
    //运行输出结果:女:21.5 男:25.0
    

OK 解决!
上述用到Stream内的方法:
groupingBy: 分组(和SQL里面个一样)
averagingInt: 对stream中的元素计算平均值

而且averaging可以操作不同的数据类型:
在这里插入图片描述

到这里我相信大家对Stream的功能以及Lambda的流式写法已经感觉很惊叹了,没事,我们继续!


三、Stream流的操作特性

  1. 不储存数据(很好理解)
  2. 不改变源数据(不管怎么操作,数据源不会改变)
  3. 不可以重复使用(为什么不可以重复使用?)

关于特性,前面两个很好理解,但为什么不能重复使用的?

首先了解一下Stream流的执行机制,其实从名字上来看,流这个字就像水流一样,会顺着一个方向流,不能倒流或者回流,所以Stream流也是如此:
在这里插入图片描述
数据源的数据转成流后,先经过过滤,过滤完后会产生一个新的流,该流一旦使用完毕就会关闭。所以排序的操作是基于前者操作结束后产生的流,后者分组也是如此,
验证:

    public static void main(String[] args) {
        Stream<Person> s1 = personList.stream();
        Stream<Person> s2 = s1.filter(s -> s.getAge() < 25);
        Stream<Person> s3 = s1.filter(s -> s.getAge() < 15);
    }

依旧是上面的List转成流s1,然后首先过滤年龄<25的,然后再用s1的流去操作年龄<15的,但是运行就会报错:

stream has already been operated upon or closed
流已被操作或关闭

由此可见,一个流一旦操作结束,就会被关闭,所以在上述代码中要使用s2继续操作


通道内的节点:

上图
在这里插入图片描述
我们把在通道内过滤、排序、分组、分页、去重的操作称为中间节点(也称懒节点),意思是被加载进去后不会立马执行,遇到终值节点才会执行。

终值节点: 只能有一个,并且放最后

验证:

    public static void main(String[] args) {
        personList.stream().filter(p->{
            System.out.println("我是中间节点");
            return true;
        });
    }

filter为中间节点,若正确,则不会执行println语句,因为该语句内没有终值节点,不会被执行,运行后控制台果真没有输出值。

那么加上一个终值节点来看看效果,比如加上toArray():

    public static void main(String[] args) {
        personList.stream().filter(p->{
            System.out.println("我是中间节点");
            return true;
        }).toArray();
    }

运行后发现执行了println语句,控制台输出了内容!

如何快速分辨中间/终值节点:

我们只需要看结构即可,通过返回的类型来判断,一般返回为Stream的都为中间节点:
在这里插入图片描述


四、流执行的顺序

通过上面流的执行机制图可以看到数据源进入流的通道内开始处理数据,但数据并不是一次性进入通道的,比如List内有五个元素,也是依次进入通道处理,最后被采集到一起

验证:
还是上面的List,若数据是一次性传入的,下面代码会先输出所有的名字,再输出所有的年龄,如果名字和年龄是依次交替的输出,则说明该List是一个元素一个元素依次传入的
peek: 在流中消耗元素时对每个元素执行附加的操作

    public static void main(String[] args) {
        personList.stream()
                .peek(p-> System.out.println(p.getName()))
                .peek(p-> System.out.println(p.getAge()))
                .toArray();
    }

运行后,控制台结果为名字和年龄是依次交替的输出, 大家可以自行去试试鸭~

注意:peek也是中间节点,所以语句末尾要加上终值节点!


流内上下节点的影响性:

流的上一个节点会影响下一个节点,比如上述代码中,我们只要输出性别为男的数据:

    public static void main(String[] args) {
        personList.stream()
                .filter(p->p.getSex().equals("男"))
                .peek(p-> System.out.println(p.getName()+":"+p.getAge())).toArray();
    }

去重distinct()的影响:
为了在列表内有重复的数据,在上面List内添加一条一样的记录

personList.add(new Person("王五", 30, "男"));

然后执行以下代码:

    public static void main(String[] args) {
        personList.stream()
                .filter(p->p.getSex().equals("男"))
                .distinct()
                .peek(p-> System.out.println(p.getName()+":"+p.getAge())).toArray();
    }

先过滤性别是男的元素,然后进行去重操作
在这里插入图片描述
通过上图可以看到每个中间节点之间数据的变化,刚开始有6个元素,第一步filter后还剩4个,然后distinct,图中可以看到把最后两个数据合并成一个数据了,说明这两者是一样的,进行了去重,最后toArray()。


下期见~~~

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

H5 WebSQL每日成语

有一天看到同事发的类似 这种成语填空一样的内容。 惟利( )视 为德( )终 质非文( ) ( )追耗子 烂漫天( ) ( )则改之&#xff0c;无则嘉勉 得( )之作 哀( )如潮 于是就去网上找成语数据库表的 SQL… 哈哈哈 很容易就找到了. 本来打算写个前后端&#xff0c;想了想可以但没…

Codeforces Round #840 (Div. 2) and Enigma 2022 - Cybros LNMIIT题解

这场好难我是废物&#xff0c;掉大分了 A. Absolute Maximization 利用|找最大值和&找最小值即可 #include <bits/stdc.h> using namespace std; // #define int long long #define endl \nvoid solve() {int n;cin >> n;int maxn 0,minn 1023;for(int i …

TCP报文结构、确认应答机制、超时重传机制、连接管理机制

TCP报文结构、确认应答机制、超时重传机制、连接管理机制一、TCP报文结构二、确认应答机制三、超时重传机制四、连接管理机制4.1 三次握手建立连接4.2 四次挥手断开连接一、TCP报文结构 可靠传输是TCP中最最核心的特性&#xff01;&#xff01;&#xff01; 上篇博客我们已经介…

企事业单位如何做好服务器数据防泄露,防病毒工作

目前现状 无论是中小企业还是大型企事业单位&#xff0c;均有属于自己的内网或公有云服务器。这些服务器有的是专门的SVN、GIT代码服务器&#xff0c;有的是文档存储服务器&#xff0c;有的是应用服务器。服务器是企业的核心命脉&#xff0c;所有知识产权及多年心血都集中汇总…

【学习笔记】JDK源码学习之HashMap(附带面试题)

最近发布 AND 其他好文 &#xff1a;地址 什么是 HashMap &#xff1f;它和 Map 之间又有什么样的关系呢&#xff1f;我们是否能拿它在并发中使用呢&#xff1f; 带着以上问题&#xff0c;让我们一同走入这让人着迷的 HashMap 。 1、什么是HashMap&#xff1f; HashMap 基于…

DOM算法系列005-获取给定节点所属的window对象

UID: 20221220091217 aliases: tags: source: cssclass: created: 2022-12-20 我们知道&#xff0c;在一个HTML页面中&#xff0c;可以包含很多个框架或框架集&#xff0c;那么&#xff0c;当给定一个节点&#xff0c;我们如何获取这个节点所属的window对象呢&#xff1f; 首先…

【填坑向】Linux获取Java程序resource下的文件路径(这次的坑好像没填住……

​╮(&#xffe3;▽&#xffe3;"")╭没错&#xff0c;就是那个传说中的FileNotFoundException&#xff0c;文件找不到了 一开始&#xff0c;特别单纯的俺是把文件直接放在项目根目录的&#xff0c;获取文件地址的位置就只写了一个文件名&#xff08;相对路径&…

Nature子刊:生命的最初几个小时内可快速学习音位辨别—基于fNIRS研究

摘要 人类新生儿可以区分音素&#xff0c;但这种能力背后的神经机制尚不清楚。新生儿的大脑可以学会区分自然&#xff08;前&#xff09;元音和逆向&#xff08;后&#xff09;元音&#xff08;在子宫中学习不到的&#xff09;。利用功能近红外光谱&#xff0c;我们检测了出生…

工控CTF之协议分析2——MMS

协议分析 流量分析 主要以工控流量和恶意流量为主&#xff0c;难度较低的题目主要考察Wireshark使用和找规律&#xff0c;难度较高的题目主要考察协议定义和特征 简单只能简单得干篇一律&#xff0c;难可以难得五花八门 常见的工控协议有&#xff1a;Modbus、MMS、MQTT、CoA…

关于3D可视化的几个知识点

3D可视化&#xff0c;运用三维仿真技术实现信息感知、智能调度以及主动式运营维护&#xff0c;把复杂抽象的数据信息&#xff0c;以合适的人们视觉元素及视角用3D立体方式进行一比一还原呈现&#xff01; 近些年&#xff0c;将繁琐的信息通过可视化的图表视觉化展现出来的应用场…

惠普Elite蜻笔记本系统损坏怎么U盘重装教学

惠普Elite蜻笔记本系统损坏怎么U盘重装教学&#xff0c;有用户使用的惠普Elite蜻笔记本系统受到了其他恶意程序的损坏&#xff0c;导致无法正常的开启使用。所以想要去进行电脑系统的重装。那么如何U盘重装电脑系统&#xff0c;一起来看看详细的重装步骤吧。 准备工作&#xff…

有哪些实用的软件开发项目管理工具?

给大家介绍下&#xff0c;目前最新的免费项目管理工具&#xff0c;希望对大家有所帮助。 1、 Co-Project智能项目管理工具 智能项目管理平台&#xff0c;是CoCode旗下的软件开发全生命周期的项目管理平台&#xff0c;涵盖了从需求、计划、设计、开发、测试再到交付的整个项目周…

青少年关联网络功能地形的性别差异

我们发现了个性化联想网络功能地形的规范性性别差异&#xff0c;包括腹侧注意、默认模式和额顶网络。此外&#xff0c;染色体富集分析显示&#xff0c;功能地形多变量模式的性别差异在空间上与x连锁基因的表达以及星形细胞和兴奋性神经元细胞类型的特征相耦合。这些结果突出了性…

实景三维模型色彩差异大,用模方与DasViewer直接对模型调色

在实际项目生产过程中&#xff0c;由于受到采集天气、设备、时间、季节、航高等各类因素的影响&#xff0c;重建后的三维模型尤其是大面积模型中&#xff0c;纹理不可避免的出现明暗不一、颜色不均匀的现象&#xff0c;导致后期处理时间与成本增高&#xff0c;影响项目工期。 三…

Apache Flink

目录 处理无边界和有边界数据 数据可以作为无边界流或有边界流处理 随时随地部署应用程序 以任何规模运行应用程序 利用内存性能 Apache Flink 起源于一个叫作 Stratosphere 的项目&#xff0c;它是由 3 所地处柏林的大学和欧洲其他一些大学共同进行的研究项目&#xff0c…

[附源码]计算机毕业设计Node.js湖南旅游景点网站(程序+LW)

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

Java码客过去的一周年

入坑伊始 2021年12月20日 我本身就是一名计算机科学与技术的大学生&#xff0c;应该也算是行内的科班出身&#xff0c;但是在2021年12月20日之前&#xff0c;也就是离19届大学生考研差不多一年的时间&#xff0c;那时候有一周的SSM网站编程实训课程&#xff0c;惊讶的是虽然学…

别再用 JWT 作为 Session 系统了,问题重重,后果很危险!

SON Web Tokens&#xff0c;又称 JWT。本文将详解&#xff1a;为何 JWT 不适合存储 Session&#xff0c;以及 JWT 引发的安全隐患。望各位对JWT有更深的理解&#xff01; 十分不幸&#xff0c;我发现越来越多的人开始推荐使用 JWT 管理网站的用户会话&#xff08;Session&…

从头到尾Spring概念,家族,核心技术使用,事务这一篇就够了!!!

目录 1.了解Spring家族 家族成员 Spring常用项目介绍 2.Spring Framework体系结构 概述 体系图 3.Spring框架提供的核心技术 IOC控制反转 1.IOC与Spring的关系 2.什么是Bean DI 依赖注入 IoC/DI技术实现 1.基于XML的实现 2.基于注解的实现 3.基于java的实现 AO…

【案例实践】InVEST实践与进阶及在生态系统服务供需、固碳、城市热岛、论文写作中的应用

【查看原文】InVEST实践与进阶及在生态系统服务供需、固碳、城市热岛、论文写作等实际项目中的具体应用 【专家】&#xff1a; 白老师&#xff08;研究员&#xff09;&#xff1a;长期从事生态系统结构-格局-过程-功能-服务的变化与响应关系等研究工作&#xff1b;重点围绕生…