Lambda表达式

慕课网Lambda笔记

第一章 Java为什么引入 Lmabda表达式

1.1 什么是Lambda表达式

Lambda表达式也被成为箭头函数、匿名函数、闭包
Lambda表达式体现的是轻量级函数式编程思想
‘->’符号是Lambda表达式的核心符号,符号左侧是操作参数,符号右侧是操作表达式

1.2 Model Code as Data

Model Code as Data,编码及数据,尽可能轻量级的将代码封装成数据。
解决方案:接口&实现了(匿名内部类)
存在问题:语法冗余、this关键字、变量捕获、数据控制等

1.3 功能接口的设计及优化

传统模式下新线程的创建
传统模式下新线程的创建
使用jdk1.8 新特性 lmabda表达式来优化线程模式
在这里插入图片描述

1.4 为什么要使用 Lmabda表达式

1.它不是解决未知问题的新技术
2. 他是对现有解决方案上语义的优化。
3. 需要根据实际需求考虑性能问题。

第二章 函数式接口的概述和定义

2.1函数式接口定义

函数式接口(functuon interface),就是Java类型系统中的接口
函数式接口,是只包含一个接口方法的特殊接口
语义化检测注解:@FunctionAllnterface,用来检查函数式接口的合法性

在这里插入图片描述在这里插入图片描述
定义一个函数式接口只需要在接口中提供一个接口方法,并在接口上添加@FunctionAllnterface注解即可,如果添加了多个饿方法,@FunctionAllnterface就会报错

2.2默认方法和静态方法

2.2.1默认接口方法的特性

以当前用户身份接口为例,创建一个实现类 返回用户的身份
在这里插入图片描述
对当前代码进行测试
在这里插入图片描述
以上代码当需求进行变动后,比如要求所有的用户验证可以同时获取用户的验证信息【是否认证成功|成功-返回用户|失败-返回null】时,我们就需要修改所有的实现类代码。这时Lambda表达式可以解决这个问题,我们可以在接口中声明一个默认方法
在这里插入图片描述
这样我们就可以直接通过代码来调用默认方法
在这里插入图片描述

2.2.2 静态接口方法的特性

以消息发送接口为例,在接口中添加一个静态方法:验证消息格式是否合法
加粗样式
添加一个实现类,重写接口中的方法
在这里插入图片描述
在format方法中,我们只是打印了一句话 消息转换。。。,并返回了msg,这时候我们可以直接调用接口的静态方法来验证消息的合法性。通过执行结果可以看出,静态方法不会对函数式接口的语义也不会产生影响,默认接口,函数式接口,静态接口可以在一个接口类中同时存在。

从Object中继承的方法不会影响函数式接口

由于java中的类都直接的或者间接的继承了Object类,所以 从Object继承的方法,无论是否是抽象的,都不会影响你函数式接口的语义。例如,在 IMessageFormat 中添加一个来自object的方法 toString(),函数式接口是不会报错的
在这里插入图片描述

2.2.3Lambda表达式和函数式接口的关系

Lambda 只能操作一个方法 Java 中的Lambda表达式就是一个函数式接口的实现

实现接口方法的另一种方式是使用匿名内部类,通过使用匿名内部类的方式来实现用户权限的验证操作。
在这里插入图片描述
执行结果:
在这里插入图片描述
通过观察匿名内部类和前面的实现方式,都会发现,其实和数据相关的代码只有
" return “admin”.equals(username) ? “超级管理员” : “普通会员”; " 这一行,其他的代码都是冗余代码,那么能不能对代码进行优化呢?这就要使用到JDK8中的Lambda表达式。
在这里插入图片描述
相比较前面的匿名内部类,Lambda表达式实现方式更为简洁
执行结果:
在这里插入图片描述

2.2.4 JDK 中常见的函数式接口

Java类型系统内建函数式接口

在这里插入图片描述

JDK8提供了java.util.function包,提供了常用的函数式功能接口

1.java.util.function.Predicate
接收参数对象T,返回一个boolean类型结果,适合需要判断的场景
在这里插入图片描述
执行结果:
在这里插入图片描述
2.java.util.function.Comsumer
接收参数T,不反回结果

在这里插入图片描述
执行结果:
在这里插入图片描述
3. java.util.function.Function<T,R>
接收一个参数对象T,返回结果对象R

在这里插入图片描述
执行结果:
在这里插入图片描述
4. java.util.function.Supplier
不接受参数,提供T对象的创建工厂

在这里插入图片描述
执行结果:
在这里插入图片描述

常见的函数式接口使用

5. java.util.function.UnaryOperator
接收参数对象T,返回结果对象T ,常用于适配器模式

在这里插入图片描述
执行结果:
在这里插入图片描述
6. java.util.function.BinaryOperator
接收两个T对象,返回一个T对象的结果(使用场景:例如对两个对象进行比较,返回较大的结果)

在这里插入图片描述
执行结果:
在这里插入图片描述

总结:

java.util.function提供了大量的函数式接口
Predicate 接收参数对象T,返回一个boolean类型结果
Comsumer 接收参数T,不反回结果
Function 接收一个参数对象T,返回结果对象R
Supplier 不接受参数,提供T对象的创建工厂
UnaryOperator 接收参数对象T,返回结果对象T
BinaryOperator 接收两个T对象,返回一个T对象的结果

2.2.5 Lmabda表达式的基本语法
			>1)声明:就是 Lambda表达式绑定的接口类型
            2)参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数顺序一致。
            3)操作符:->
            4)执行代码块:包含在一对大括号中,出现在操作符的右侧
             [接口声明] = (参数) ->{执行代码}
没有返回值的Lambda表达式

在这里插入图片描述
如果在执行代码块中,只有一行代码,大括号是可以省略的
在这里插入图片描述
执行结果:
在这里插入图片描述

带有参数 但是没有返回值得Lambda 表达式和接口

带有参数时,要将参数写在小括号中,参数的顺序和接口中定义的顺序相同
在这里插入图片描述
在设置参数时,也可以不写参数的类型,JVM会自动推断出参数的类型,以下方式和上面的代码是一样的
在这里插入图片描述
执行结果:
在这里插入图片描述

带有参数,带有返回值得Lambda表达式

在这里插入图片描述
当花括号内只有一行代码时,可以不添加花括号,同时,也不需要添加 return 关键字,虚拟机会自动帮你返回
在这里插入图片描述
执行结果:
在这里插入图片描述

总结

1 Lambda 表达式,必须和接口进行绑定
2 Lambda 表达式的参数,可以附带0到n个参数,括号中的参数类型可以不用指定,JVM在运行时,会自动根据绑定的抽象方法进行推导
3 Lambda 表达式的返回值,如果代码快只有一行并且没有大括号,不用写大括号,单行代码会自动返回,如果添加了大括号,或者代码有多行代码,必须通过return 关键字返回结果

3.6变量访问
匿名内部类中的变量访问

在这里插入图片描述

lamdba表达式对于变量的访问

在这里插入图片描述

Lambda表达式类型检查
表达式类型检查

定义一个函数式接口 MyInterface,接收两个范型R,T,并提供一个方法 strategy 接受参数T 返回参数R
在这里插入图片描述
定义一个方法test,接受一个Myinterface作为参数,范型为String 和List,在方法内部我们将String 添加到List集合中
在这里插入图片描述
分别使用匿名内部类 和Lambda表达式的方式调用test
在这里插入图片描述
在Lambda表达式的写法中,并没有指明方法的参数为Myinterface,而是直接传递了参数X,y,这是由底层虚拟机来自动推导出来的。

执行结果
在这里插入图片描述

总结

当我们使用Lambda表达式语法的时候,jvm会获取当前方法的参数来进行推导,从而自动为我们绑定方法的参数类型.这就是Lambda表达式的类型检查

3.7方法重载和Lambda表达式

首先创建一个类App4,在App4中创建两个接口 Parame1 和Parame2,并定义outInfo(String info) 方法
在这里插入图片描述
然后定义重载方法lambdaMethod,参数分别是Parame1和Parame2
在这里插入图片描述
使用传统的匿名内部类的方式调用,在main方法中创建App4的对象,使用对象点lambdaMethdo的方式new一个Parame1或者Parame2
在这里插入图片描述
执行结果:
在这里插入图片描述
使用lambda表达式的话因为是重载方法,jvm自动推导类型的时候类型检查不通过,会报如下错误
在这里插入图片描述
在这种情况下只能使用匿名内部类来替代lambda表达式

3.8深入理解Lambda表达式

3.8.1Lambda表达式低层解析运行原理

创建一个类App,并创建一个用于Lambda表达式执行的函数式接口IMakeUP,提供一个方法makeUp(String msg),在main方法中创建Lambda表达式打印msg
在这里插入图片描述
编译后,我们可以看到生成了App.class文件和IMakeUP.class文件
在这里插入图片描述
通过使用 javap -p App.class 命令对class文件进行反编译得到结果
在这里插入图片描述
Lambda表达式在JVM低层解析成私有静态方法和匿名内部类型

通过实现接口的匿名内部类型中接口方法调用静态实现方法,完成Lambda表达式的执行

第四章 Lambda表达式在集合中的运用

4.1 方法引用

  • 方法引用是结合Lambda表达式的一种语法特性,

首先创建一个测试类Test, 在里面创建一个内部类Person,添加属性 name(名字),gender(性别),age(年龄),并使用lombok创建get/set方法和构造函数
在这里插入图片描述

  • 静态方法引用

原始方式 类型名称.方法名称() -->类型名称::方法名称
初始化一些数据,并对数据进行排序
在这里插入图片描述
使用匿名内部类的方式进行排序
在这里插入图片描述
执行结果:
在这里插入图片描述
Lambda表达式的实现方式
在这里插入图片描述
执行结果:
在这里插入图片描述
静态方法引用

在Person类中添加一个静态方法comperByAge
在这里插入图片描述
在这里插入图片描述
执行结果:
在这里插入图片描述

  • 实例方法引用

创建类型对应的对象 -->对象引用::实例方法名称

在Test下创建一个新类PersonUtils,添加一个方法comerByName(),根据人员的名称的hash值来进行排序
在这里插入图片描述
在这里插入图片描述
执行结果:
在这里插入图片描述

  • 构造方法引用

构造方法的引用需要绑定一个函数式接口

首先创造一个函数式接口
加粗样式
在这里插入图片描述

4.2 Steam概述

什么是Steam
Steam是Java为了操作数组,集合来进行复杂的聚合操作而推出的一套新的API
新创建一个测试类Test2 创建一个main方法,在main方法中初始化一个字符串集合

1.要求长度大于等于五的内容为有效账号
在这里插入图片描述
循环方式:
在这里插入图片描述
迭代器方式
在这里插入图片描述
使用Steam结合Lambda表达式的方式
在这里插入图片描述
注意:这三种方式的性能是相同的,只是精简了代码长度

4.3 SteamAPI

4.3.1Steam聚合操作

什么是聚合操作?

在常规业务处理中,针对业务的批量操作,例如,在电商项目中 ,获取指定数据的年平均消费额,获取指定店铺中最便宜的商品,获取指定店铺的当月有效订单数量等等

Steam的处理流程
获取数据源->数据转换(可以执行一次或者多次)->获取结果

4.3.2获取Steam对象

从集合,数组中获取

Collection.steam(),如上一节中的account.steam(); //从集合中获取Steam对象
Collection.parallelSteam(); //获取到一个支持并发的Steam对象;
Arrs.Stream(T t); //从数组中获取Stream对象

从缓冲流中获取

BufferReader
BufferReader.lines()->stream();

静态工厂

java.util.stream.Intstream().range()…
java.nio.file.Files.work()…

自行构建

java.util.Spliterator

更多的方式

Random.ints()
Pattern.splitAsStream()…

4.3.3 Stream操作类型

Stream的操作类型主要分为两种主要类型和一种辅助类型

中间操作API

API:intermediate中间:记录操作[无状态|有状态]

它的操作结果是一个Stream对象,中间操作可以有一个或者多个连续的中间操作.

需要注意的是,所有的中间操作,只记录操作方式,不做具体执行,直到结束操作执行时,才做数据的最终执行.中间操作就是业务的逻辑处理

中间操作的过程分为有状态和无状态的操作.

无状态: 数据处理时,不受前置中间操作的影响,就是前面的中间操作不会对当前的中间操作产生影响.
无状态API:
map
filter
peek
parallel
sequential
unordered

有状态: 数据处理时,会受到前一个中间操作的影响.
例如,前一个中间操作是一个排序操作,当前中间操作是一个截取操作,有状态下,当前操作会对排序后的结果进行截取.
有状态API:
distinct
sorted
limit
skip

结束操作(终结操作)(Terminal)

一个Stream只能有一个终结操作,一旦执行终结操作,Stream就会真实处理数据,生成对应的处理结果,并且这个结果是不可逆的…
终结操作又区分为:短路和非短路操作. 短路操作和非短路操作是根据处理结果来定义的

非短路操作:Stream对象必须处理完集合中所有的数据,才能返回处理结果
非短路操作API:
forEach
forEachOrdered
toArray
reduce
collect
min
max
count
iterator

短路操作:当前的Strame对象在处理过程中,一旦满足某个条件,就可以获取结果,并不需要处理所有的数据
短路操作API:
anyMatch
allMatch
noneMatch
findFirst
findAny等
短路操作也被称作:Short-circuiting.
什么时候使用短路操作:
例如从一个无限大的Stream中返回一个有限大的Stream

4.4 Steam操作集合中的数据

4.1.1将多个数据转换为Stream对象

多个数据转换得到Stream对象

在这里插入图片描述

数组转换Stream对象

在这里插入图片描述

列表转换Stream对象

在这里插入图片描述

集合操作

在这里插入图片描述

Map操作

在这里插入图片描述

4.1.2Stream 对于基本数据类型的功能性封装

针对于基本数据类型Strame在运算时会进行频繁的装箱拆箱,所以对于基本数据类型进行功能性封装
只针对于常用的 int lang double 类型 ,其他的没有

以int为例
在这里插入图片描述

4.1.3Stream转换得到指定的数据类型(数组,集合,字符串,map)

数组

在这里插入图片描述

字符串

在这里插入图片描述
执行结果:
在这里插入图片描述

列表

在这里插入图片描述
执行结果:
在这里插入图片描述

集合

在这里插入图片描述
>执行结果:
在这里插入图片描述

Map

在这里插入图片描述
执行结果:
在这里插入图片描述
在这里,Collectors.toMap方法需要传入一个Function,这个接口在处理的过程中会得到两个不同的数据 ,由于我们的stream
中只包含了一个单个数据,所以我们在处理的过程中需要将Map中的数据进行单独的处理

在这里插入图片描述

注意:由于stream一旦进行终结操作,那么久意味着整个过程的结束,所以上述代码无法一次性全部执行,只能在执行一个的时候,将其他代码注释掉.

4.1.4 Stream常见的API操作

准备数据

在这里插入图片描述

在每个数据的前面增加一个字段,梁山好汉

在这里插入图片描述
执行结果:
在这里插入图片描述

添加过滤条件,来过滤符合条件的用户

在这里插入图片描述
执行结果:
在这里插入图片描述

循环遍历

在这里插入图片描述

使用peek来进行多次的迭代操作

在这里插入图片描述
通过peek可以对数据进行多次的迭代操作,由于peek是中间操作,所以在peek的过程中不会遍历数据,只有等到执行终结操作的时候才会处理数据,所以虽然进行了三次peek,但实际上只进行了一次迭代操作.

stream对于数字运算的支持

构建数据

在这里插入图片描述

skip()

skip操作是一个中间操作,他是一个有状态的操作 跳过部分数据,完成数据的提取

在这里插入图片描述
执行结果
在这里插入图片描述

limit()

limit 操作是一个中间操作,他是一个有状态的操作 限制输出数据的输出数量

先跳过3个数据在限制输出两个数据
在这里插入图片描述
执行结果:
在这里插入图片描述

distinct()

distinct 操作是一个中间操作,他是一个有状态的操作 去除重复数据

在这里插入图片描述
执行结果:
在这里插入图片描述
初始化数据的时候忘了加重复元素…所以这的结果和原来的数据一样 emmmm…

sorted()

sorted 操作是一个中间操作,他是一个有状态的操作 对数据进行排序

x-y是从小到大排序, y-x是从大到小排序
在这里插入图片描述
执行结果
在这里插入图片描述

max()

max 操作是一个中间操作,他是一 个有状态的操作 获取最大值

如果改变了x-y的顺序的话得到的结果也会改编.调用max后会返回一个Optional对象,调用Optional.get()即可获取结果
在这里插入图片描述
执行结果:
在这里插入图片描述

min()

minx 操作是一个中间操作,他是一个有状态的操作 获取最小值

如果改变了x-y的顺序的话得到的结果也会改编.调用min后会返回一个Optional对象,调用Optional.get()即可获取结果
在这里插入图片描述
执行结果
在这里插入图片描述

reduce()

reduce 操作是一个中间操作,他是一个有状态的操作 合并处理数据

这里对数据做了一个累加,即前面的数据想加合并,然后在与下一个数据想加调用reduce后会返回一个Optional对象,调用Optional.get()即可获取结果,这里直接使用方法链调用了
在这里插入图片描述
执行结果:
在这里插入图片描述

5 Lambda表达式在实际生产中的应用
5.1Lambda表达式重构项目

5.2 Lambda和Stream的性能问题

1 通过基本数据类型:整数进行测试
创建一个Integer list 集合,添加一些随机数集合数据
在这里插入图片描述

性能测试
1 stream 对象

创建一个方法testStreame 接收一个list集合方法,通过stream对象获取最大值
在这里插入图片描述
执行时间 : 641ms
在这里插入图片描述

2.for循环

创建一个方法testForloop 接收一个list集合方法,通过stream对象获取最大值
在这里插入图片描述
执行时间:94 ms
在这里插入图片描述

3 parallelstream

创建一个方法testParallelstream 接收一个list集合方法,通过stream对象获取最大值在这里插入图片描述
执行时间: 169ms
在这里插入图片描述

4 增强型for循环
创建一个方法testStrongstream 接收一个list集合方法,通过stream对象获取最大值![在这里插入图片描述](https://img-blog.csdnimg.cn/20200503202317431.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NjEyNzU1,size_16,color_FFFFFF,t_70)
执行时间:83 ms

在这里插入图片描述

5 迭代器操作

创建一个方法testIterator接收一个list集合方法,通过stream对象获取最大值
在这里插入图片描述
执行时间: 74ms
在这里插入图片描述

总结:串行的Stream 的执行时间比较长,并行的Stream和增强for很接近了,\

2 通过复杂数据类型:对象进行测试

创建一个对象产品.并增加一个全属性的构造方法
在这里插入图片描述
我们来测试获取热度最高的产品对象

创建一个对象集合,并初始化数据
在这里插入图片描述

通过五种方式对获取热度最高的产品进行测试
1 Stream

在这里插入图片描述执行时间 :380ms
在这里插入图片描述

2 parallelstream

在这里插入图片描述
执行时间:90ms
在这里插入图片描述

3 for

在这里插入图片描述
执行时间:110ms
在这里插入图片描述

4 增强for

在这里插入图片描述
执行时间85ms’
在这里插入图片描述

5迭代器

在这里插入图片描述
执行时间 70ms
在这里插入图片描述
总结:在处理对象的过程中,串行Stream 依旧是执行时间最长的,parallelstream的处理事件已经可以和迭代器媲美,比普通的for要高,最快的还是迭代器.和增强for

5.3 Stream 的线程安全问题
1 Stream并行原理

在这里插入图片描述
通过整数列表的并行复制来测试Stream是否存在安全问题
初始化一个整数集合,分别通过串行Stream和并行Stream来进行集合间的复制,并打印源集合和复制后的集合的长度
在这里插入图片描述
执行结果:
在这里插入图片描述
源数据长度为1000,串行的Stream的长度没变,但是并行的Stream的集合长度变小了.说明并行Stream存在线程安全问题.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值