Lambda表达式超详解
目录
背景
Lambda表达式的用法
函数式接口
Lambda表达式的基本使用
语法精简
变量捕获
匿名内部类
匿名内部类中的变量捕获
Lambda的变量捕获
Lambda表达式在类集中的使用
Collection接口
List接口
Map接口
总结
背景
Lambda表达式是Java SE 8中的一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口.lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块).Lambda表达式基于数学中的λ演算得名,也可以称为闭包.
Lambda表达式的用法
基本语法:(parameters)->expression或(parameters)->{statements;}
Lambda表达式由三个部分组成:
1.parameters:类似方法中的形参列表,这里的参数是函数式接口里的参数.这里的参数类型可以明确声明也可不声明而由JVM隐含的判断.另外当只有一个推断类型时可以省略掉圆括号.
2.->:可理解为被用于的意思
3.方法体:可以是表达式也可以代码块,是在函数式接口里方法的实现.代码块可返回一个值或什么都不返回,这里的代码块等同于方法中的方法体,如果是表达式,可以返回一个值或者什么都不返回.
举个栗子:
//1.不需要参数,返回值为2
()->2
//2.接收一个参数(数字类型),返回其两倍的值
x -> 2 * x
//3.接收两个参数(数字),并返回它们的和
(x, y) -> x + y
//4.接收2个int型整数,返回它们的乘积
(int x, int y) -> x * y
//5.接收一个string对象,并在控制台打印,不返回任何值(看起来是返回void)
(String s) -> System.out.println(s)
函数式接口
要了解Lambda表达式,首先需要了解什么是函数式接口,函数接口定义:一个接口有且只有一个抽象方法.
注意:
1.如果一个接口有且只有一个抽象方法,那么该接口就是函数式接口
2.如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的.所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解.加上就会自动进行检测的.
定义方式:
@FunctionalInterface
interface NoParameterNoReturn {//注意:只能有一个方法void test();
}
但是这种方法也是可以的:
@FunctionalInterface
interface NoParameterNoReturn {void test();default void test2() {System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");}
}
Lambda表达式的基本使用
首先,我们事先准备好几个接口:
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {void test();
}//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a, int b);
}//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {int test();
}//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {int test(int a, int b);
}
我们在上面提到过,Lambda可以理解为:Lambda就是匿名内部类的简化,实际上是创建了一个类,实现了接口,重写了接口的方法.
没有使用lambda表达式时的调用方式:
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){@Overridepublic void test() {System.out.println("hello");}
};
具体使用见以下代码:
public class TestDemo {public static void main(String[] args) {NoParameterNoReturn noParameterNoReturn = () -> {System.out.println("无参数无返回值");};noParameterNoReturn.test();OneParameterNoReturn oneParameterNoReturn = (int a) -> {System.out.println("一个参数一个返回值: " + a);};oneParameterNoReturn.test(10);MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {System.out.println("多个参数无返回值: " + a + " " + b);};moreParameterNoReturn.test(20, 30);NoParameterReturn noParameterReturn = () -> {System.out.println("有返回值无参数! ");return 40;};//接收函数的返回值int ret = noParameterReturn.test();System.out.println(ret);OneParameterReturn oneParameterReturn = (int a) -> {System.out.println("有返回值且有一个参数");return a;};ret = oneParameterReturn.test(50);System.out.println(ret);MoreParameterReturn moreParameterReturn = (int a, int b) -> {System.out.println("有返回值且有多个参数");return a + b;};ret = moreParameterReturn.test(70, 60);System.out.println(ret);}
}
语法精简
1.参数类型可以省略,如果需要省略,每个参数的类型都需要省略.
2.参数的小括号里面只有一个参数,那么小括号可以省略.
3.如果方法体中只有一句代码,那么大括号可以省略.
4.如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字.
示例代码:
public static void main(String[] args) {MoreParameterNoReturn moreParameterNoReturn = (a, b) -> {System.out.println("无返回值多个参数,省略参数类型: " + a + " " + b);};moreParameterNoReturn.test(20, 30);OneParameterNoReturn oneParameterNoReturn = a -> {System.out.println("无参数一个返回值, 小括号可以省略: " + a);};oneParameterNoReturn.test(10);NoParameterNoReturn noParameterNoReturn = () -> System.out.println("无参数无返回值,方法体中只有一行代码");noParameterNoReturn.test();//方法体中只有一条语句,且是return语句NoParameterReturn noParameterReturn = () -> 40;int ret = noParameterReturn.test();System.out.println(ret);}
变量捕获
Lambda表达式中存在标量捕获,了解变量捕获之后,我们才能更好地理解Lambda表达式的作用域.
Java当中的匿名类中,会存在变量捕获.
匿名内部类
匿名内部类就是没有名字的内部类.我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用即可,那么下面来简单看看匿名内部类的使用就好了.
举个简单的例子:
class Test {public void func() {System.out.println("func()");}
}public class TestDemo {public static void main(String[] args) {Test t = new Test() {@Overridepublic void func() {System.out.println("我是匿名内部类,且重写了func这个方法! ");}};t.func();}
}
在上述代码当中的main函数当中,我们看到的就是一个匿名内部类的简单使用.
匿名内部类中的变量捕获
在匿名内部类中,在使用内外的变量时,有两个条件:(要么是常量,要么是未修改的变量)
来看一下代码:
public class TestDemo1 {public static void main(String[] args) {int a = 100;Test t1 = new Test() {@Overridepublic void func() {System.out.println("我是匿名内部类,且重写了func这个方法!");System.out.println("我是捕获到变量a==" + a + "我是一个常量, 或者是一个没有改变过值的变量! ");}};t1.func();}
}
在上述代码当中变量a就是捕获的变量.这个变量要么是被final修饰,如果不是被final修饰的 你要保证在使用之前没有修改.如下代码就是错误的代码.
public class TestDemo2 {public static void main(String[] args) {int a = 100;Test t2 = new Test() {@Overridepublic void func() {a = 99;System.out.println("我是匿名内部类,且重写了func这个方法!");System.out.println("我是捕获到变量a==" + a + "我是一个常量, 或者是一个没有改变过值的变量! ");}};}
}
该代码直接编译报错.
Lambda的变量捕获
lambda其实就是可以认为是匿名内部类的实现
在lambda表达式中也可以进行变量的捕获,具体我们看一下代码.
@FunctionalInterface
interface NoParameterNoReturn {void test();
}public class TestDemo3 {public static void main(String[] args) {int a = 10;NoParameterNoReturn noParameterNoReturn = () -> {//a = 99;errorSystem.out.println("捕获变量" + a);};noParameterNoReturn.test();}
}
Lambda表达式在类集中的使用
为了能够让Lambda和java的集合类更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接.
对应的接口 | 新增的方法 |
Collection | removelf() spliterator() stream() parallelStream() forEach() |
List | replaceAll() sort() |
Map | getOrDefault() forEach() replaceAll() putlfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge() |
以上方法的作用可自行查看javaapi.
Collection接口
forEach()方法演示
该方法在接口Iterable当中,原型如下:
default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for(T t : this) {action.accept(t);}
}
该方法表示:对容器中的每个元素执行action指定动作.
public class TestDemo4 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");list.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {//简单遍历集合中元素System.out.println(s + " ");}});}
}
输出结果:
我们可以修改为如下代码:
public class TestDemo4 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");//表示调用一个,不带有参数的方法,其执行花括号内的语句,为原来的函数体内容.list.forEach(s -> System.out.println(s));}
}
List接口
sort()方法的演示
sort()方法源码:该方法根据c指定的比较规则对容器元素进行排序.
public void sort(Comparator<? super E> c) {final int expectedModCount = modCount;Arrays.sort((E[]) elementData, 0, size, c);if(modCount != expectedModCount) {throw new ConcurrentModificationExpection();}modCount++;
}
使用示例:
public class TestDemo5 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("hello");list.add("bit");list.add("lambda");System.out.println(list);list.sort(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {//这里比较长度return o1.length() - o2.length();}});System.out.println(list);}
}
输出结果:
修改为lambda表达式:
public class TestDemo5 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("hello");list.add("bit");list.add("lambda");System.out.println(list);//调用带有两个参数的方法,且返回长度的差值list.sort((str1, str2) -> str1.length() - str2.length());System.out.println(list);}
}
Map接口
HashMap的forEach()
该方法的原型如下:
default void forEach(BiConsumer<? super K, ? super V> action) {Objects.requireNonNull(action);for(Map.Entry<K, V> entry : entrySet()) {K k;V v;try {k = entry.getKey();v = entry.getValue();} catch {//this usually means the entry is no longer in the map.throw new ConcurrentModificationExpection(ise);}action.accept(k, v);}
}
作用是对Map中的每个映射执行action指定操作.
代码示例:
public class TestDemo6 {public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "bit");map.put(3, "hello");map.put(4, "lambda");map.forEach(new BiConsumer<Integer, String>() {@Overridepublic void accept(Integer integer, String s) {System.out.println(integer + " = " + s);}});}
}
输出结果:
使用lambda表达式之后的代码:
public class TestDemo6 {public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "bit");map.put(3, "hello");map.put(4, "lambda");map.forEach((k, v) -> System.out.println(k + " = " + v));}
}
总结
lambda表达式的优点很明显,在代码层次上来说,使代码变得非常地简洁.缺点也不明显,代码不易读.
优点:
1.代码简洁,开发迅速
2.方便函数式编程
3.非常容易进行并行运算
4.Java引入了Lambda,改善了集合操作
缺点:
1.代码可读性变差
2.在非并行计算中,很多计算未有传统的for性能要高
3.不容易进行调试
相关文章:

Lambda表达式超详解
目录 背景 Lambda表达式的用法 函数式接口 Lambda表达式的基本使用 语法精简 变量捕获 匿名内部类 匿名内部类中的变量捕获 Lambda的变量捕获 Lambda表达式在类集中的使用 Collection接口 List接口 Map接口 总结 背景 Lambda表达式是Java SE 8中的一个重要的新特性.…...

西门子博途与菲尼克斯无线蓝牙模块通讯
菲尼克斯无线蓝牙模块 正常运行时,可以使用基站控制字0发送00E0(得到错误代码命令) 正常运行时,可以使用基站控制字0发送00E0(得到错误代码命令)得到各个无线I/O是否连 接的信号(状态字IN word 1的第2、6、10位) 小车1连接状态 小车2连接状态 小车3连接状态 1#小车自…...

vue2 之 实现pdf电子签章
一、前情提要 1. 需求 仿照e签宝,实现pdf电子签章 > 拿到pdf链接,移动章的位置,获取章的坐标 技术 : 使用fabric pdfjs-dist vuedraggable 2. 借鉴 一位大佬的代码仓亏 : 地址 一位大佬写的文章 :地址 3. 优化 在大佬的代码…...

什么是MVC?MVC框架的优势和特点
目录 一、什么是MVC 二、MVC模式的组成部分和工作原理 1、模型(Model) 2、视图(View) 3、控制器(Controller) 三、MVC模式的工作过程如下: 用户发送请求,请求由控制器处理。 …...
主从复制mysql-replication | Replication故障排除
主从复制mysql-replication 准备环境 #防火墙 selinux systemctl stop firewalld --now &&setenforce 0 #修改主机名:hostnamectl set-hostname 名字 tip:vim /etc/sysconfig/network-scripts/ifcfg-ens33 BOOTPRTOTstatic IPADDR192.168.100.…...

基于Java SSM框架实现教学质量评价评教系统项目【项目源码+论文说明】计算机毕业设计
基于java的SSM框架实现教学质量评价评教系统演示 摘要 随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,教学质量评价系统当然也不能排除在外。教学质量评价系统是以实际运用为…...

03|模型I/O:输入提示、调用模型、解析输出
03|模型I/O:输入提示、调用模型、解析输出 从这节课开始,我们将对 LangChain 中的六大核心组件一一进行详细的剖析。 模型,位于 LangChain 框架的最底层,它是基于语言模型构建的应用的核心元素,因为所谓 …...

springcloud-gateway-2-鉴权
目录 一、跨域安全设置 二、GlobalFilter实现全局的过滤与拦截。 三、GatewayFilter单个服务过滤器 1、原理-官方内置过滤器 2、自定义过滤器-TokenAuthGatewayFilterFactory 3、完善TokenAuthGatewayFilterFactory的功能 4、每一个服务编写一个或多个过滤器,…...

实现一个最简单的内核
更好的阅读体验,请点击 YinKai s Blog | 实现一个最简单的内核。 这篇文章带大家实现一个最简单的操作系统内核—— Hello OS。 PC 机的引导流程 我们这里将借助 Ubuntu Linux 操纵系统上的 GRUB 引导程序来引导我们的 Hello OS。 首先我们得了解一下&a…...

2024华为OD机试真题指南宝典—持续更新(JAVAPythonC++JS)【彻底搞懂算法和数据结构—算法之翼】
PC端可直接搜索关键词 快捷键:CtrlF 年份关键字、题目关键字等等 注意看本文目录-快速了解本专栏 文章目录 🐱2024年华为OD机试真题(马上更新)🐹2023年华为OD机试真题(更新中)🐶新…...

【12.23】转行小白历险记-算法02
不会算法的小白不是好小白,可恶还有什么可以难倒我这个美女的,不做花瓶第二天! 一、螺旋矩阵 59. 螺旋矩阵 II - 力扣(LeetCode) 1.核心思路:确定循环的路线,左闭右开循环,思路简…...
k8s部署nginx-ingress服务
k8s部署nginx-ingress服务 经过大佬的拷打,终于把这块的内容配置完成了。 首先去 nginx-ingress官网查看相关内容。 核心就是这个: kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/prov…...

SpringBoot Elasticsearch全文搜索
文章目录 概念全文搜索相关技术Elasticsearch概念近实时索引类型文档分片(Shard)和副本(Replica) 下载启用SpringBoot整合引入依赖创建文档类创建资源库测试文件初始化数据创建控制器 问题参考 概念 全文搜索(检索),工作原理:计算…...
Python 常用模块re
Python 常用模块re 【一】正则表达式 【1】说明 正则表达式是一种强大的文本匹配和处理工具,主要用于字符串的模式匹配、搜索和替换。正则表达式测试网址:正则表达式在线测试 正则表达式手册:正则表达式手册 【2】字符组 字符转使用[]表…...
【华为OD题库-106】全排列-java
题目 给定一个只包含大写英文字母的字符串S,要求你给出对S重新排列的所有不相同的排列数。如:S为ABA,则不同的排列有ABA、AAB、BAA三种。 解答要求 时间限制:5000ms,内存限制:100MB 输入描述 输入一个长度不超过10的字符串S,确保都是大写的。…...
Three.js 详细解析(持续更新)
1、简介; Three.js依赖一些要素,第一是scene,第二是render,第三是carmea npm install --save three import * as THREE from "three"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js&quo…...

Unity中Shader平移矩阵
文章目录 前言方式一:对顶点本地空间下的坐标进行相加平移1、在属性面板定义一个四维变量记录在 xyz 上平移多少。2、在常量缓冲区进行申明3、在顶点着色器中,在进行其他坐标转化之前,对模型顶点本地空间下的坐标进行转化4、我们来看看效果 方…...

python dash 的学习笔记1
dash 用python开发web界面 https://dash.plotly.com/ 官方上支持jula F# python一类。当然我只会python只学习python中使用dash. 要做一个APP,用php,java以及.net都可以写,只所有选择python是因为最近在用这一个。同时也发现python除了慢全是优点。 资料…...
SQLITE如何同时查询出第一条和最后一条两条记录
一个时间记录表,需要同时得到整个表或一段时间内第一条和最后一条两条记录,按如下方法会提示错误:ORDER BY clause should come after UNION not before select * from sdayXX order by op_date asc limit 1 union select * from sday…...

四、ensp配置ftp服务器实验
文章目录 实验内容实验拓扑操作步骤配置路由器为ftp server 实验内容 本实验模拟企业网络。PC-1为FTP 用户端设备,需要访问FTP Server,从服务器上下载或上传文件。出于安全角度考虑,为防止服务器被病毒文件感染,不允许用户端直接…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...