Lambda表达式Stream流-函数式编程入门教程

目录

函数式编程思想

Lambda 表达式

Stream流

常用的Stream中的API

创建Stream

第一种是直接使用.Stream()的方式来创建

第二种采用.of()方式创建

具体来看看每一个API是怎么用的

concat

max

min

findFirst

findAny

count

peek

 forEach

forEachOrdered

skip

distinct

map

flatMap

 collection

toArray

reduce


函数式编程思想

        在数学中函数就是有输入量 , 输出量的一套计算方案, 也就是拿什么东西做什么事情. 相对而言, 面向对象过分强调"必须通过对象的形式来做事情", 而函数式 思想则尽量忽略面向对象的复杂语法, 更加强调做什么,而不是以什么形式做

面向对象的思想 :

        ​做一件事情, 找一个能解决这个事情的对象, 调用对象的方法, 完成事情.

函数式编程思想 :

        ​ 只要能获取到结果, 谁去做的, 怎么做的都不重要. 重视的是结果, 不重视过程

Lambda 表达式

        Lambda 表达式是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象,是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包。

        最常见的一个例子就是新建线程,有时候为了省事,会用下面的方法创建并启动一个线程,这是匿名内部类的写法,new Thread需要一个 implements 自Runnable类型的对象实例作为参数,比较好的方式是创建一个新类,这个类 implements Runnable,然后 new 出这个新类的实例作为参数传给 Thread。而匿名内部类不用找对象接收,直接当做参数。

 new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程中run的方法实现了");
            }
        }).start();

但是这样写的话是不是觉得很土,不简洁,所以这个时候Lambda表达式就派上了用场

 new Thread(() -> {
            System.out.println("新线程中run的方法实现了");
        }).start();

小技巧: 可以使用alt+enter快速将匿名内部类和lambda之间相互转换

Lambda 表达式简化了匿名内部类的形式,可以达到同样的效果,但是 Lambda 要优雅的多。虽然最终达到的目的是一样的,但其实内部的实现原理却不相同。匿名内部类在编译之后会创建一个新的匿名内部类出来,而 Lambda 是调用 JVM invokedynamic指令实现的,并不会产生新类。

Stream流

        Stream流不只是Lambda表达式厉害,它其中还包含了很多 Java 8 中集合数据处理的利器,很多本来复杂、需要写很多代码的方法,比如过滤、分组等操作,往往使用 Stream 就可以在一行代码搞定,当然也因为 Stream 都是链式操作,一行代码可能会调用好几个方法。

        Collection接口提供Stream()方法,让我们可以对集合使用Stream API来进行各种操作。要注意的是对同一个集合进行stream操作,执行的任何操作不会对源集合产生任何影响,所以对于同一个集合你可以采用多个stream来进行操作。

        我们先来看看Stream流的接口定义,继承至BaseStream,所有的接口声明都是接收方法引用类型的参数,比如 filter方法,接收了一个 Predicate类型的参数,它就是一个函数式接口,常用来作为条件比较、筛选、过滤用,JPA中也使用了这个函数式接口用来做查询条件拼接。

常用的Stream中的API

常用的API还是有一点多,可以稍微看看下面这张图

 

创建Stream

创建Stream的方式有很多,这里介绍两种常见的:

第一种是直接使用.Stream()的方式来创建

第二种采用.of()方式创建

可能你会疑惑为什么这么写,那最好的方式就是来看看源代码:

 参数和返回值都是接受的一个泛型,所以无论你传入的什么都是可以的,需要注意的是, stream流创建后只能使用一次,创建了一个stream流使用两次会报  IllegalStateException异常。  

具体来看看每一个API是怎么用的
concat

连接两个 Stream ,不改变其中任何一个 Steam 对象,返回一个新的 Stream 对象。

private static void concatStream(){ 
    Stream<String> a = Stream.of("y","yy","yyy"); 
    Stream<String> b = Stream.of("d","e"); 
    Stream<String> c = Stream.concat(a,b); 
}
max

一般用于求数字集合中的最大值,或者按实体中数字类型的属性比较,拥有最大值的那个实体。它接收一个 Comparator<T>,上面也举到这个例子了,它是一个函数式接口类型,专门用作定义两个对象之间的比较,例如下面这个方法使用了 Integer::compareTo这个方法引用。

public static void maxi(){
        Stream<Integer> stream=Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 9);
        Integer reMax=stream.max(Integer::compareTo).get();
        System.out.println(reMax);
    }

 

当然,我们也可以自己定制一个 Comparator,顺便复习一下 Lambda 表达式形式的方法引用。

 public static void maxi(){
        Stream<Integer> stream=Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 9);
        Comparator<Integer> comparator=(x,y)->(x.intValue()<y.intValue()?-1:(x.intValue()==y.intValue()?0:1));
        Integer reMax=stream.max(comparator).get();
        System.out.println(reMax);
    }
min

与 max 用法一样,只不过是求最小值。

findFirst

获取 Stream 中的第一个元素。

findAny

获取 Stream 中的某个元素,如果是串行情况下,一般都会返回第一个元素,并行情况下就不一定了。

count

返回元素个数。

peek

建立一个通道,在这个通道中对 Stream 的每个元素执行对应的操作,对应 Consumer<T>的函数式接口,这是一个消费者函数式接口,顾名思义,它是用来消费 Stream 元素的,比如下面这个方法,把每个元素转换成对应的大写字母并输出。

 public static void testPeek(){
        Stream<String> stream=Stream.of("a","b","c");
        List<String> list=stream.peek(c-> System.out.println(c.toUpperCase())).collect(Collectors.toList());
//        list.stream().forEach(System.out::println);
    }
 forEach

和 peek 方法类似,都接收一个消费者函数式接口,可以对每个元素进行对应的操作,但是和 peek 不同的是,forEach 执行之后,这个 Stream 就真的被消费掉了,之后这个 Stream 流就没有了,不可以再对它进行后续操作了,而 peek操作完之后,还是一个可操作的 Stream 对象。

正好借着这个说一下,我们在使用 Stream API 的时候,都是一串链式操作,这是因为很多方法,比如接下来要说到的 filter方法等,返回值还是这个 Stream 类型的,也就是被当前方法处理过的 Stream 对象,所以 Stream API 仍然可以使用。

forEachOrdered

功能与 forEach是一样的,不同的是,forEachOrdered是有顺序保证的,也就是对 Stream 中元素按插入时的顺序进行消费。为什么这么说呢,当开启并行的时候,forEachforEachOrdered的效果就不一样了。

skip

跳过前 n 条数据,例如下面代码,返回结果是 c。

distinct

元素去重,例如下面方法返回元素是 a、b、c,将重复的 b 只保留了一个。

map

map方法的接口方法声明如下,接受一个 Function函数式接口,把它翻译成映射最合适了,通过原始数据元素,映射出新的类型。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

 Function的声明也是这样的,接收一个T型的参数,返回一个R型的参数,这样就可以实现元素类型的转换,用于将当前类型转换为另一个合适的类型

flatMap

当你的 Stream 是以下这几种结构的时候,需要用到 flatMap方法,用于将原有二维结构扁平化。

  1. Stream<String[]>
  2. Stream<Set<String>>
  3. Stream<List<String>>

以上这三类结构,通过 flatMap方法,可以将结果转化为 Stream<String>这种形式,方便之后的其他操作。

比如下面这个方法,将List<List<User>>扁平处理,然后再使用 map或其他方法进行操作。

 public static void testFlatMap(){
        List<String> list=Arrays.asList("a1","a2","b1","b2","c2");
        List<String> list1=Arrays.asList("a","b","c");
        List<List<String>> list2=new ArrayList<>();
        list2.add(list);
        list2.add(list1);
        Stream<List<String>> stream=list2.stream();
        List<String> stream1=stream.flatMap(Collection::stream).collect(Collectors.toList());
        stream1.forEach(System.out::println);
    }
 collection

  Collectors为我们提供了很多拿来即用的收集器。比如我们经常用到Collectors.toList()Collectors.toSet()Collectors.toMap()。另外还有比如Collectors.groupingBy()用来分组,比如下面这个例子,按照 userId 字段分组,返回以 userId 为key,List 为value 的 Map,或者返回每个 key 的个数。

// 返回 userId:List<User>
Map<String,List<User>> map = user.stream().collect(Collectors.groupingBy(User::getUserId));

// 返回 userId:每组个数
Map<String,Long> map = user.stream().collect(Collectors.groupingBy(User::getUserId,Collectors.counting()));
toArray

collection是返回列表、map 等,toArray是返回数组,有两个重载,一个空参数,返回的是 Object[]

另一个接收一个 IntFunction<R>类型参数。

reduce

它的作用是每次计算的时候都用到上一次的计算结果,比如求和操作,前两个数的和加上第三个数的和,再加上第四个数,一直加到最后一个数位置,最后返回结果,就是 reduce的工作过程。

 public static void testReduce(){
        Stream<Integer> stream=Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 9);
        Integer re=stream.reduce(1,(x,y)->x*y);
        System.out.println(re);
    }

当然还有几个map有关的函数也是比较重要的,这里就不做过多的介绍了~

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

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

相关文章

oracle 9i 行头带有scn的表

oracle 9i 行头带有scn的表 conn scott/tiger drop table t1; drop table t2; create table t1(c varchar2(5)); create table t2(c varchar2(6)) ROWDEPENDENCIES; --t2表每行都有scn,会增加六个字节的开销 alter table t1 pctfree 0; alter table t2 pctfree 0; insert in…

Array.map解析

map方法会创建一个新数组。该方法会循环数组中的每个值&#xff0c;如果仅仅是想循环数组不需要返回值使用数组的forEach方法就可以。原数组中的每个元素都调用一次提供的函数后的返回值组成。Array.map 它接收一个函数 这个函数可以接收三个参数 数组的每个值item 这个值的索引…

2016-2021年全国范围的2.5m分辨率的建筑屋顶数据

一、论文介绍 摘要&#xff1a;大规模且多年的建筑屋顶面积&#xff08;BRA&#xff09;地图对于解决政策决策和可持续发展至关重要。此外&#xff0c;作为人类活动的细粒度指标&#xff0c;BRA可以为城市规划和能源模型提供帮助&#xff0c;为人类福祉带来好处。然而&#xf…

【html项目】教你制作地表最强王者辅助网站装逼神器

今天如何教你们写教你们如何写一个 仿王者荣耀修改器装逼神器 效果图 html: <body><header> <a href"//pvp.qq.com/" class"qqq" title"王者荣耀" onclick"PTTSendClick(link,logo,logo);">王者荣耀</a>&l…

信息系统架构模型_2.面向服务架构(SOA)模式

前面讲的客户机/服务器模式&#xff0c;无论多少层的C/S软件结构&#xff0c;对外来讲&#xff0c;都只是一个单结点应用&#xff08;无论它由多个不同层的“服务”相互配合来完成其功能&#xff09;&#xff0c;具体表现为一个门户网站、一个应用系统等。而多个单点应用相互通…

到东莞樟木头“中国作家第一村”来!这里大有文“樟”

樟木头&#xff0c;古称泰安&#xff0c;一直是康泰平安、物阜民丰之地。作为东莞唯一纯客家镇&#xff0c;传自中原先民的烂漫因子让这座城市崇文重礼&#xff0c;绿水青山更氤氲出古镇芳华。这个文章锦绣地&#xff0c;以其敢为人先、勇立潮头的姿态&#xff0c;成为了各种文…

Python专题:八、列表(1)

Python的内置数据类型 数据类型&#xff1a;列表 list类型 可以是字符串&#xff0c;浮点数&#xff0c;整数&#xff0c;列表 列表特性 ①集合性的数据类型 ②列表是有序的 ③列表是可更新的 访问列表元素的方式也是[索引]&#xff0c;也是从0开始的&#xff0c;不能超过…

【姿态解算与滤波算法】

姿态解算 一、主线 姿态表示方式&#xff1a;矩阵表示&#xff0c;轴角表示&#xff0c;欧拉角表示&#xff0c;四元数表示。 惯性测量单元IMU&#xff08;Inertial Measurement Unit&#xff09;&#xff1a;MPU6050芯片&#xff0c;包含陀螺仪和加速度计&#xff0c;分别测…

ROS2 工作空间

文章目录 ROS2 工作空间创建工作空间自动安装依赖编译工作空间设置环境变量参考链接 ROS2 工作空间 工作空间可以简单理解为工程目录。 ROS 系统中一个典型的工作空间结构如图所示&#xff1a; dev_ws&#xff1a; 根目录&#xff0c;里面会有四个子目录&#xff08;子空间&a…

net 7部署到Linux并开启https

1.修改代码设置后台服务运行 安装nuget包 Install-Package Microsoft.Extensions.Hosting.WindowsServices Install-Package Microsoft.Extensions.Hosting.Systemd在Program代码中设置服务后台运行 var builder WebApplication.CreateBuilder(args);if (System.OperatingS…

鸿蒙开发接口Ability框架:【DataAbilityHelper模块(JS端SDK接口)】

DataAbilityHelper模块(JS端SDK接口) 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 本模块接口仅可在FA模型下使用。 使用说明 使用前根据具体情况引入如下模块 import featureAbility from …

跳跃游戏 II解题思路详解

题解 跳跃游戏 II &#x1f91a;我的博客&#x1f95b;前言 跳跃游戏 II描述示例提示 题解初步思考思路细节代码实现完整代码 END&#x1f4a0;END&#x1f3d5;️公众号 &#x1f91a;我的博客 欢迎光临我的博客&#xff1a;https://blog.csdn.net/qq_52434217?typeblog &a…

两个系统中的数据匹配方法

一、首先介绍几种常用的相似度计算 1.1最长公共子序列(LCS) 最长公共子序列&#xff08;Longest Common Subsequence&#xff0c;简称LCS&#xff09;是在两个或多个序列中寻找最长的公共子序列的问题。这里所说的“子序列”指的是原序列中元素的子集&#xff0c;但保持元素的原…

暗区突围资格 暗区突围测试资格 暗区突围资格申请

《暗区突围》作为一款备受瞩目的战术射击手游&#xff0c;以其独特的撤离玩法、高度拟真的枪战体验以及丰富的装备搜集系统&#xff0c;在玩家群体中迅速积累了极高的人气。游戏设定在一个充满未知与危险的封闭区域&#xff0c;玩家需要凭借智慧、策略与精湛的操作&#xff0c;…

使用./build.sh编译ORB_SLAM源码时出现报错:/usr/bin/ld:找不到 -lboost_serialization的解决办法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、/usr/bin/ld:找不到 -lboost_serialization1.问题描述2.解决(1). 下载源码(2) . 编译安装 一、/usr/bin/ld:找不到 -lboost_serialization 1.问题描述 在安装…

DPDK e1000 ring buffer

基本原理 如图&#xff08;盗图&#xff09; 内存&#xff08;RAM&#xff09;和网卡&#xff08;NIC&#xff09;之间通过Descriptor ring 交互网络报文数据内存中需要申请内存 packet buffer 的内存池&#xff0c;内存池中的每个实例&#xff0c;地址是物理连续的或者IOVA…

day3_prefixSum

一、前缀和技巧 重点 前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和 个人理解&#xff1b;预计算&#xff0c;空间换时间 1.(一维数组的前缀和)303区域和检索-数组不可变 获取闭区间值 [left,right] -> preSum[right 1] - preSum[left],其中preSum[right…

泵站远程启停

随着物联网技术的迅猛发展&#xff0c;传统泵站的管理方式正面临前所未有的变革。在这一变革的浪潮中&#xff0c;HiWoo Cloud平台凭借其卓越的技术实力和创新理念&#xff0c;为泵站远程启停控制带来了全新的解决方案。本文将详细介绍HiWoo Cloud平台在泵站远程启停方面的应用…

基于Python的飞机大战游戏

学习目标 了解 飞机大战游戏的规则 理解 面向对象思想,会独立设计游戏的类与模块 掌握 pygame模块的使用 1.1 游戏介绍 飞机大战是一款由腾讯公司微信团队推出的软件内置的小游戏,这款游戏画面简洁有趣,规则简单易懂,操作简便易上手,在移动应用兴起之初曾风靡一时。 1.1.…

Navicat导出表结构到Excel或Word

文章目录 sql语句复制到excel复制到Word sql语句 SELECTcols.COLUMN_NAME AS 字段,cols.COLUMN_TYPE AS 数据类型,IF(pks.CONSTRAINT_TYPE PRIMARY KEY, YES, NO) AS 是否为主键,IF(idxs.INDEX_NAME IS NOT NULL, YES, NO) AS 是否为索引,cols.IS_NULLABLE AS 是否为空,cols.…