深入学习Java中的Lambda表达式
深入学习Java中的Lambda表达式
自Java 8引入以来,Lambda表达式彻底改变了Java的编程风格,让代码变得更加简洁、易读,尤其是在函数式编程的场景中。接下来,我们将深入探讨Lambda表达式的语法、原理以及实际应用,帮助你更好地理解并运用这一强大的工具。
文章目录
- 深入学习Java中的Lambda表达式
- 1. Lambda表达式的基本语法
- 1.1 无参数的Lambda表达式
- 1.2 一个参数的Lambda表达式
- 1.3 多个参数的Lambda表达式
- 2. Lambda表达式的原理
- 2.1 Lambda表达式的实现机制:函数式接口
- 2.2 编译器生成的匿名类
- 2.3 方法引用(Method References)
- 2.4 Lambda表达式与接口方法的绑定
- 2.5 JVM与Lambda的实现:Invokedynamic
- 2.6 Lambda表达式与性能
- 3. Lambda表达式的应用
- 3.1 配合Stream API处理集合数据
- 3.2 Lambda表达式简化匿名内部类
- 4. 总结
1. Lambda表达式的基本语法
(parameters) -> expression
或者如果需要更复杂的代码块:
(parameters) -> { statements }
parameters
:Lambda表达式的输入参数,可以有一个或多个。若只有一个参数,可以省略圆括号;如果没有参数或有多个参数,必须使用圆括号。->
:箭头符号,用于分隔参数和表达式。expression
:Lambda体,即表达式或代码块,定义了Lambda的实现逻辑。如果只有一个表达式,Java会自动返回该表达式的值;如果是一个代码块,则必须显式地使用return
语句。
1.1 无参数的Lambda表达式
如果没有参数,必须使用圆括号。
Runnable r = () -> System.out.println("Hello, Lambda!");
r.run(); // 输出 "Hello, Lambda!"
Runnable
是一个没有输入参数的函数接口,因此我们使用()
表示没有参数,->
后跟要执行的代码块。
1.2 一个参数的Lambda表达式
如果只有一个参数,可以省略括号。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");names.forEach(name -> System.out.println(name)); // 输出每个名字
- 这里的Lambda表达式接收一个
name
参数,并执行System.out.println(name)
。它是forEach
方法的一个参数,用来对列表中的每个元素进行操作。
1.3 多个参数的Lambda表达式
当有多个参数时,必须使用括号。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);int sum = numbers.stream().reduce(0, (a, b) -> a + b); // 求和操作
System.out.println(sum); // 输出 15
reduce
方法接收两个参数,第一个是初始值0
,第二个是Lambda表达式(a, b) -> a + b
,它将集合中的所有数字相加。
2. Lambda表达式的原理
2.1 Lambda表达式的实现机制:函数式接口
Lambda表达式主要应用于函数式接口,即只包含一个抽象方法的接口。例如:
@FunctionalInterface
public interface MyFunction {int apply(int a, int b);
}
Lambda表达式在Java中通常是与函数式接口(只有一个抽象方法的接口)结合使用的。Java在编译时会将Lambda表达式转换为该接口的实现,并使用动态代理机制生成实现类。
2.2 编译器生成的匿名类
Lambda表达式并不是直接创建一个类,而是通过一种称为匿名类的机制在幕后创建。事实上,Lambda表达式的内部实现往往会通过编译器生成一个匿名类或方法的形式来实现接口的抽象方法。
例如,以下的Lambda表达式:
(a, b) -> a + b
会被编译器转换为类似这样的一种形式:
new MyFunction() {@Overridepublic int apply(int a, int b) {return a + b;}
}
但是这种匿名类并不会像传统的匿名类那样显式地出现在代码中,而是由Java的虚拟机(JVM)在运行时动态创建。
2.3 方法引用(Method References)
Lambda表达式可以与方法引用配合使用,进一步简化代码。例如,你可以将一个Lambda表达式转换为对已有方法的引用,从而避免编写冗余代码。
// 使用Lambda表达式
Function<String, Integer> stringLength = s -> s.length();// 使用方法引用
Function<String, Integer> stringLengthMethodRef = String::length;
方法引用本质上是一个对某个方法的引用,JVM会在运行时将其与Lambda表达式的功能关联。
2.4 Lambda表达式与接口方法的绑定
Lambda表达式与接口的抽象方法是通过目标类型推断来绑定的。具体来说,JVM会根据Lambda表达式所使用的接口类型来推断出Lambda表达式的目标类型。
例如,考虑以下代码:
List<String> names = Arrays.asList("Tom", "Jerry", "Mickey");
names.forEach(name -> System.out.println(name));
在这段代码中,forEach
方法接受一个Consumer<T>
消费类型的接口。Lambda表达式name -> System.out.println(name)
的目标类型会被推断为Consumer<String>
接口的方法accept()
。
2.5 JVM与Lambda的实现:Invokedynamic
Java 8引入了**invokedynamic
**字节码指令,这是Lambda表达式的核心机制之一。invokedynamic
指令允许JVM在运行时动态地绑定和执行代码,这意味着在运行时,Lambda表达式的具体实现才会被确定。
Lambda表达式通过以下几个步骤实现:
- 编译阶段:Lambda表达式在编译时会被转换成特定的字节码,其中包括一个方法句柄,该方法句柄指向Lambda表达式所实现的接口方法。
- 运行时:JVM通过
invokedynamic
指令来延迟方法绑定。这使得Lambda表达式的实际调用能够在运行时动态决定,从而提高性能。 - 方法引用:Lambda表达式还可以被实现为方法引用。方法引用本质上也是一种对Lambda表达式的优化,因为它不需要通过Lambda表达式生成新的对象或匿名类实例,而是直接引用现有的方法。
2.6 Lambda表达式与性能
Lambda表达式在性能上并不会带来显著的开销。由于Lambda表达式通常会被编译为方法句柄,且通过invokedynamic
指令动态绑定方法,JVM的优化使得Lambda表达式的性能与传统的匿名类相比几乎没有差别,甚至可能更高效。
但是需要注意的是,Lambda表达式的实际性能仍然取决于具体的使用场景。例如,在一些频繁调用的场景中,Lambda表达式可能会有一些性能上的开销,但总体来说,这种开销是微乎其微的。
3. Lambda表达式的应用
Lambda表达式在Java中广泛应用,特别是在处理集合、流操作等场景中。通过简洁的语法,Lambda表达式使得代码更加清晰、简洁,同时提升了可读性和可维护性。
3.1 配合Stream API处理集合数据
在Stream
API中,Lambda表达式非常适合用于处理集合数据的过滤、映射、排序等操作。比如,我们可以使用Lambda表达式配合Stream
API筛选出集合中的某些元素。下面是一个示例,展示如何使用Lambda表达式筛选出所有偶数。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 使用Lambda表达式过滤出偶数
numbers.stream().filter(n -> n % 2 == 0) // 过滤出偶数.forEach(System.out::println); // 输出结果
numbers.stream()
:将numbers
列表转化为一个Stream
流,Stream流支持更丰富的函数式操作。.filter(n -> n % 2 == 0)
:这是一个Lambda表达式,用于过滤出所有偶数。filter
方法会遍历流中的每个元素,只有符合条件(即n % 2 == 0
)的元素会被保留。.forEach(System.out::println)
:对每一个经过过滤后的元素执行println
操作,这里使用方法引用System.out::println
,其等价于n -> System.out.println(n)
,简化了代码。
3.2 Lambda表达式简化匿名内部类
在Java 8之前,匿名内部类是实现接口或抽象类的主要方式。Lambda表达式使得这些代码更加简洁。以下是Comparator
排序的匿名内部类和Lambda表达式的对比:
使用匿名内部类:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return s1.compareTo(s2); // 按字母顺序排序}
});
使用Lambda表达式:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, (s1, s2) -> s1.compareTo(s2)); // 按字母顺序排序
- 匿名内部类代码较长,需要声明一个新的类,并重写
compare
方法。 - Lambda表达式简洁,直接用
(s1, s2) -> s1.compareTo(s2)
来实现Comparator
的compare
方法。
4. 总结
Lambda表达式是Java 8引入的一个功能,它通过简洁的语法允许你编写匿名函数,主要用于函数式接口(接口中只有一个抽象方法)的实现,Lambda有着以下特性:
- 简洁性:Lambda表达式可以让代码更简洁、更直观,减少了冗余的类和方法声明。
- 函数式编程支持:Lambda表达式支持函数式编程,尤其是和Java 8的Stream API结合使用时,可以进行复杂的数据操作,如过滤、映射、排序等。
- 延迟执行:在Stream的操作中,Lambda表达式通常是惰性求值的,这意味着只有在最终操作(如
forEach
、collect
等)触发时,数据才会被实际处理。
本文是在GPT的帮助下进行整理与归纳的,如果对你有帮助,欢迎点赞、留言与转发。
相关文章:

深入学习Java中的Lambda表达式
深入学习Java中的Lambda表达式 自Java 8引入以来,Lambda表达式彻底改变了Java的编程风格,让代码变得更加简洁、易读,尤其是在函数式编程的场景中。接下来,我们将深入探讨Lambda表达式的语法、原理以及实际应用,帮助你…...
1.2 AI 量化炒股的起源与发展
**定性价值**:AI量化炒股通过算法模型实现投资决策自动化,显著提升交易效率与风险控制能力,打破传统人工交易的主观性与延迟性,推动金融科技向智能化、数据驱动方向迭代,具有颠覆传统投资模式的战略意义。 **定量价值…...

计算机单位之详解——存储单位Byte 网络传输单位bps 视频码率单位bps
前言: 计算机里面单位有点复杂,容易混淆,很多时候混起来就容易概念不理解,包括一些小问题,比如说:为什么我买了1T硬盘,实际存在虚标。为什么所谓的千兆宽带,下载起来没有1G每秒&…...

IDEA关闭SpringBoot程序后仍然占用端口的排查与解决
IDEA关闭SpringBoot程序后仍然占用端口的排查与解决 问题描述 在使用 IntelliJ IDEA 开发 Spring Boot 应用时,有时即使关闭了应用,程序仍然占用端口(例如:4001 端口)。这会导致重新启动应用时出现端口被占用的错误&a…...

deepseek清华大学第二版 如何获取 DeepSeek如何赋能职场应用 PDF文档 电子档(附下载)
deepseek清华大学第二版 DeepSeek如何赋能职场 pdf文件完整版下载 https://pan.baidu.com/s/1aQcNS8UleMldcoH0Jc6C6A?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/3ee62050a2ac...
【python随手记】——读取文本文件内容转换为json格式
文章目录 前言一、TXT文件转换为JSON数组1.txt文件内容2.python代码3.输出结果 二、TXT文件转换为JSON对象1.txt文件2.python代码3.输出结果 前言 场景:用于读取包含空格分隔数据的TXT文件,并将其转换为结构化JSON文件 一、TXT文件转换为JSON数组 1.tx…...

k8s集群3主5从高可用架构(kubeadm方式安装k8s)
关键步骤说明 环境准备阶段 系统更新:所有节点执行yum/apt update确保软件包最新时间同步:通过ntpdate time.windows.com或部署NTP服务器网络规划:明确划分Service网段(默认10.96.0.0/12)和Pod网段(如Flann…...

基于 sklearn 的均值偏移聚类算法的应用
基于 sklearn 的均值偏移聚类算法的应用 在机器学习和数据挖掘中,聚类算法是一类非常重要的无监督学习方法。它的目的是将数据集中的数据点划分为若干个类,使得同一类的样本点彼此相似,而不同类的样本点相互之间差异较大。均值偏移聚类&…...

三、大模型微调的多种方法与应用场景
详解大模型微调的多种方法与应用场景 随着大模型的不断发展,如何有效地微调这些庞大的预训练模型以适应特定任务成为了研究和应用中的一个重要问题。大模型微调不仅能够提高任务性能,还能在不同的业务需求中提升模型的适应性。在本文中,我们…...
第2课 树莓派镜像的烧录
树莓派的系统通常是安装在SD卡上的。SD卡作为启动设备,负责启动树莓派并加载操作系统。这种设计使得树莓派具有便携性和灵活性,用户可以通过更换SD卡来更换操作系统或恢复出厂设置。 烧录树莓派的镜像即是将树莓派镜像烧录到SD卡上,在此期间会格式化SD卡,如果SD卡…...

SQL之order by盲注
目录 一.order by盲注的原理 二.注入方式 a.布尔盲注 b.时间盲注 三.防御 一.order by盲注的原理 order by子句是用于按指定列排序查询结果,列名或列序号皆可。 order by 后面接的字段或者数字不一样,那么这个数据表的排序就会不同。 order by 盲…...

AI大模型(四)基于Deepseek本地部署实现模型定制与调教
AI大模型(四)基于Deepseek本地部署实现模型定制与调教 DeepSeek开源大模型在榜单上以黑马之姿横扫多项评测,其社区热度指数暴涨、一跃成为近期内影响力最高的话题,这个来自中国团队的模型向世界证明:让每个普通人都能…...

java后端开发day19--学生管理系统升级
(以下内容全部来自上述课程) 1.要求及思路 1.总体框架 2.注册 3.登录 4.忘记密码 2.代码 1.javabean public class User1 {private String username;private String password;private String personID;private String phoneNumber;public User1() {…...
MFC文件和注册表的操作
MFC文件和注册表的操作 日志、操作配置文件、ini、注册表、音视频的文件存储 Linux下一切皆文件 C/C操作文件 const char* 与 char* const const char* 常量指针,表示指向的内容为常量。指针可以指向其他变量,但是内容不能再变了 char szName[6]&qu…...

vscode如何使用鼠标滚轮调整字体大小
1.打开设置 2.搜索Font Ligatures 3.编辑配置文件 4.修改代码并保存 修改前 修改后 在最后一行添加:“editor.mouseWheelZoom”: true 记得在上一行最后,加上英文版的“,”逗号 5.配置成功,再次按Ctrl鼠标滚轮便可以缩放了。...
C++之vector和list辨析
std::vector 和 std::list 是 C 标准库中两种常用的容器,它们都用于存储和管理元素集合,但在底层实现和性能特性上有显著的区别。 1. 底层实现 std::vector: 基于动态数组实现。元素在内存中是连续存储的。支持随机访问(通过下标访问元素&a…...

冯诺依曼体系结构 ──── linux第8课
目录 冯诺依曼体系结构 关于冯诺依曼,必须强调几点: 冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系 输入单元:包括键盘, 鼠标,网卡,扫…...
EX_25/2/22
找到第一天mystring练习,实现以下功能 mystring str "hello" mystring ptr "world" str str ptr; str ptr str[0] H #include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #in…...

rust安装教程以及git连接到远程仓库
1.官方网站下载rustup-init程序 链接: rust-lang 从这里可以获取到rust的下载程序,这个下载程序会帮助你下载visual-studio的安装包从而获取相关的编译环境。 tips:无需再下载visual_studio 2确认安装所需要的框架,SKD工具 安装完毕之后可以检查一下 rustc --ve…...

Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因
Kafka系列之:记录一次源头数据库刷数据,造成数据丢失的原因 一、背景二、查看topic日志信息三、结论四、解决方法一、背景 源头数据库在很短的时间内刷了大量的数据,部分数据在hdfs丢失了 理论上debezium数据采集不会丢失,就需要排查数据链路某个节点是否有数据丢失。 数据…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...