【Java高级语法】(十五)lambda表达式:给你一颗语法糖Lambda,解析函数式编程的杰作~

news2024/11/20 3:19:29

Java高级语法详解之lambda表达式

  • 1️⃣ 概念
  • 2️⃣ 优势和缺点
  • 3️⃣ 使用
    • 3.1 语法结构
    • 3.2 案例
      • 3.2.1 无参Lambda
      • 3.2.2 带有一个参数
      • 3.2.3 带有多个参数
      • 3.2.4 方法引用的简化形式
  • 4️⃣ 应用场景
  • 5️⃣ 优化技巧
  • 6️⃣ 原理
  • 7️⃣ 注意性能问题
  • 🌾 总结

在这里插入图片描述

1️⃣ 概念

Java Lambda表达式是在Java 8中引入的一项重要特性。它们主要受到函数式编程语言的影响,如Haskell和Scala。Lambda表达式为Java引入了一种简洁而强大的功能,可以更便捷地处理函数式编程的概念。

Lambda表达式是一个匿名函数,可以传递给方法作为参数或用作返回值。它是一个使用箭头符号(->)定义的代码块,由Lambda参数、箭头符号和方法体组成。Lambda表达式允许我们将行为作为一等公民进行传递,使得代码更具可读性和灵活性。

2️⃣ 优势和缺点

优点:

  • 简洁性:Lambda表达式允许以更紧凑的方式编写代码,减少样板代码;
  • 使代码更易读:通过使用Lambda表达式,可以将关注点放在实际要执行的操作上,减少了对底层实现的关注;
  • 支持函数式编程风格:能够更好地支持函数式编程的思想,如高阶函数、闭包等。

缺点:

  • 需要理解函数式编程的概念:Lambda表达式需要开发者掌握函数式编程的思想,在初步学习过程中可能较为陌生;
  • 可能引发性能问题:Lambda表达式在某些情况下可能导致额外的开销和性能损失,特别是与传统的循环和条件语句相比。

3️⃣ 使用

3.1 语法结构

Lambda表达式的语法结构如下:

(parameters) -> expression or {statements}

这是最基本的Lambda表达式结构,其中,parameters表示方法参数列表,可以为空或包含一个或多个参数,expression或者{statements}表示要执行的表达式或代码块,用代码块来表示多个语句时,可以在其中包含多条语句并使用return语句返回结果。

而针对不同的情况,Java中Lambda表达式的语法结构有以下几种写法:

  1. 无参情况:当Lambda表达式不需要参数时,可以使用一对空括号代替参数列表,后面可以跟一个表达式或一个代码块。
    () -> expression or {statements}
    
  2. 带有一个参数的情况:当Lambda表达式只有一个参数时,可以省略参数列表的括号。
    parameter -> expression or {statements}
    
  3. 带有多个参数的情况:
    (parameters) -> expression or {statements}
    
  4. 方法引用的简化形式:如果Lambda表达式的主体只是调用一个方法,可以使用方法引用的简化形式来代替具体的Lambda表达式,其中Class是静态方法所在的类名,object是实例方法所在的对象。
    Class::methodName
    object::methodName
    

这些是Lambda表达式的常见写法,在应用程序中根据需求和上下文选择合适的写法。

关于方法引用的知识在后边的文章再具体介绍。

3.2 案例

3.2.1 无参Lambda

public class LambdaExample {
    public static void main(String[] args) {
        // 1. 使用Lambda表达式实现Runnable接口
        Runnable runnable = () -> System.out.println("Hello, World!");
        Thread thread = new Thread(runnable);
        thread.start();
        
        // 2. 使用Lambda表达式实现自定义函数式接口
        MyFunctionalInterface funcInterface = () -> System.out.println("Hello, ChinaAi!");
        funcInterface.run();
    }
}

@FunctionalInterface
interface MyFunctionalInterface {
    void run();
}

在上面代码中,我使用Lambda表达式实现Runnable接口:创建一个Runnable对象,Lambda表达式表示了在run()方法中要执行的代码块。

然后,我使用Lambda表达式实现自定义的函数式接口MyFunctionalInterface:创建一个实现该接口的对象,Lambda表达式表示了在run()方法中要执行的代码块。

运行结果:

Hello, World!
Hello, ChinaAi!

3.2.2 带有一个参数

import java.util.function.Consumer;

public class LambdaExample {
    public static void main(String[] args) {
        // 1. 使用Lambda表达式实现Consumer接口
        Consumer<String> consumer = (name) -> System.out.println("Hello, " + name);
        consumer.accept("Alice");
        
        // 2. 使用Lambda表达式实现自定义函数式接口
        MyFunctionalInterface funcInterface = (name) -> System.out.println("Hello, " + name);
        funcInterface.run("Bob");
    }
}

@FunctionalInterface
interface MyFunctionalInterface {
    void run(String name);
}

在上面代码中,我使用Lambda表达式实现Consumer接口:创建一个Consumer对象,Lambda表达式表示了在accept()方法中要执行的代码块。

然后,我使用Lambda表达式实现自定义的函数式接口MyFunctionalInterface:创建一个实现该接口的对象,Lambda表达式表示了在run(String name)方法中要执行的代码块。

运行结果:

Hello, Alice
Hello, Bob

3.2.3 带有多个参数

import java.util.Comparator;

public class LambdaExample {
    public static void main(String[] args) {
        // 1. 使用Lambda表达式实现Comparator接口
        Comparator<Integer> comparator = (num1, num2) -> Integer.compare(num1, num2);
        int result = comparator.compare(5, 3);
        System.out.println(result);
        
        // 2. 使用Lambda表达式实现自定义函数式接口
        MyFunctionalInterface funcInterface = (num1, num2) -> System.out.println("Sum: " + (num1 + num2));
        funcInterface.run(10, 20);
    }
}

@FunctionalInterface
interface MyFunctionalInterface {
    void run(int num1, int num2);
}

在上面代码中,我使用Lambda表达式实现Comparator接口:创建一个Comparator对象,Lambda表达式表示了在compare(num1, num2)方法中要执行的代码块。其中,调用了静态方法Integer.compare()来比较两个整数的大小。

然后使用Lambda表达式实现自定义的函数式接口MyFunctionalInterface:创建一个实现该接口的对象,Lambda表达式表示了在run(int num1, int num2)方法中要执行的代码块。

运行结果:

1
Sum: 30

3.2.4 方法引用的简化形式

import java.util.function.Consumer;
import java.util.function.Supplier;

public class LambdaExample {
    public static void main(String[] args) {
        // 1. 对象::实例方法
        Consumer<String> consumer1 = System.out::println;
        consumer1.accept("Hello, World!");
        
        // 2. 类名::静态方法
        Supplier<Double> supplier = Math::random;
        double randomNum = supplier.get();
        System.out.println(randomNum);
       
    }
}

在上面代码中,我使用 对象::实例方法 来创建一个Consumer对象,使用对象引用方法的简化形式来执行accept()方法。
使用 类名::静态方法 来创建一个Supplier对象,使用类名引用静态方法的简化形式来执行get()方法。

运行结果:

Hello, World!
0.6158534899118129

4️⃣ 应用场景

Lambda表达式在多种场景下都能发挥作用,包括但不限于:

  • 函数式接口(Functional Interface):通过Lambda表达式可以实现函数式接口的匿名内部类替代;
  • 集合操作:使用Lambda表达式可以在集合上进行筛选、映射、归约等操作,让代码更加简洁和易读;
  • 多线程编程:使用Lambda表达式可以方便地实现多线程任务的并行处理;
  • GUI事件处理:Lambda表达式可以简化GUI应用程序中的事件处理代码。

5️⃣ 优化技巧

在编写和使用Lambda表达式时,可以考虑以下优化技巧:

  • 避免过度复杂化:尽量保持Lambda表达式简洁,以提高代码可读性。
  • 使用方法引用:当Lambda表达式仅仅是调用一个已经存在的方法时,可以使用方法引用来代替,使得代码更加简洁。
  • 多线程优化:结合并行流(parallel stream)和Lambda表达式,可以实现多核处理器的并行计算,提高效率。

6️⃣ 原理

Java语言中的lambda表达式是一种闭包(Closure)的实现方式,它允许我们将函数作为参数传递给其他方法,并且能够在需要的时候延迟执行这些代码。

Lambda表达式最初在Java 8中引入,主要用于简化匿名内部类的编写,使得代码更加简洁、易读。Lambda表达式被视为一种可传递的代码块,充当了函数式接口的一个实例。

Lambda表达式的原理基于如下几个关键概念:

  1. 函数式接口:Lambda表达式的类型由目标上下文推断得出,并且该上下文必须是函数式接口。函数式接口是只包含一个抽象方法的接口。
  2. Lambda表达式语法:Lambda表达式可以通过箭头符号(->)来定义,左侧表示输入参数,右侧表示方法体。例如:(参数) -> { 方法体 }
  3. 类型推断:Java编译器使用目标上下文,根据参数类型进行类型推断,以确定Lambda表达式的类型。
  4. 闭包与捕获变量:Lambda表达式可以访问其周围的局部变量和参数,甚至可以访问final或事实上是final的变量。这些被访问的变量称为捕获变量,并且它们会在形成闭包时被复制。

当使用Lambda表达式时,编译器将根据语法规则将其转化为对应的函数式接口实例。在运行时,可以像传递常规对象一样传递和执行Lambda表达式。这种方式使得我们可以更方便地使用函数式编程风格来处理集合、并发等问题。

7️⃣ 注意性能问题

Java中的lambda表达式在某些情况下可能导致性能问题。以下是几种可能的情况:

  1. 过多的自动装箱和拆箱:Lambda表达式中的参数和返回值类型是通过自动装箱和拆箱来实现的。如果在频繁调用的场景中使用Lambda表达式,会产生大量的对象创建和销毁操作,从而引起性能问题。

  2. 频繁创建匿名内部类:每次使用Lambda表达式时,都会创建一个匿名内部类的实例。如果在循环或高并发应用中频繁创建大量的匿名内部类实例,会导致不必要的开销,影响性能。

  3. 循环中的Lambda表达式:如果在循环中使用Lambda表达式,每次迭代都会创建一个新的Lambda实例。对于大型循环,特别是多层嵌套的循环,这可能导致频繁的垃圾回收和额外的开销,降低性能。

  4. Lambda表达式的逻辑复杂臃肿:如果Lambda表达式内部包含大量的复杂逻辑操作,例如循环、条件判断等,可能会导致Lambda执行时的时间复杂度增加,从而降低性能。

为了避免上述问题,可以采取以下对应策略:

  • 避免在性能敏感的代码块中过度使用Lambda表达式,尤其是频繁调用的地方。
  • 尽量重复使用Lambda实例,避免频繁创建匿名内部类。
  • 对于循环内的Lambda表达式,可以考虑将其提取到循环外部,以减少实例的创建次数。
  • 简化并优化Lambda表达式的逻辑,减少复杂度和执行时间。

同时,最重要的是需要根据具体场景和性能需求评估使用Lambda表达式可能带来的性能影响,并进行必要的优化措施。

🌾 总结

Java Lambda表达式为Java 8引入了强大的功能,通过简洁的语法使开发者能够更高效地编写代码。无论是在集合操作、多线程编程还是GUI事件处理中,都可以灵活应用Lambda表达式。但需要注意的是,在某些情况下它们可能导致性能问题,因此需要谨慎使用。

在这里插入图片描述

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

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

相关文章

架构设计第十一讲:架构之高并发:限流

架构设计第十一讲&#xff1a;架构之高并发&#xff1a;限流 每个系统都有服务的上线&#xff0c;所以当流量超过服务极限能力时&#xff0c;系统可能会出现卡死、崩溃的情况&#xff0c;所以就有了降级和限流。限流其实就是&#xff1a;当高并发或者瞬时高并发时&#xff0c;为…

rabbitmq第三课-RabbitMQ高级功能详解以及常用插件实战

一、选择合适的队列. 实际上是可以选择三种队列类型的&#xff0c;classic经典队列&#xff0c;Quorum仲裁队列&#xff0c;Stream流式队列。 后面这两种队列也是RabbitMQ在最近的几个大的版本中推出的新的队列类型。3.8.x推出了Quorum仲裁队列&#xff0c;3.9.x推出了Stream流…

MyBatis何时使用一级缓存,何时使用二级缓存?

Mybatis设计2级缓存来提升数据检索效率&#xff0c;避免每次都查询数据库。 一、一级缓存 一级缓存 Mybatis 的一级缓存是指 SQLSession&#xff0c;一级缓存的作用域是 SQlSession , Mabits 默认开启一级缓存。 在同一个SqlSession中&#xff0c;执行相同的SQL查询时&#x…

基于STM32CUBEMX驱动TOF模块VL6180与VL6180X(2)----修改测量范围

概述 当使用VL6180传感器进行测距时&#xff0c;可以通过修改缩放因子来改变可测量的距离范围。VL6180是一种基于飞行时间原理的传感器&#xff0c;通过测量光信号的往返时间来确定物体与传感器之间的距离。 默认情况下&#xff0c;VL6180传感器的测距范围约为0至200毫米。然…

显卡检测工具:GPU-Z

今天小编为大家测试了一款轻量级的GPU显卡的测试工具&#xff0c;可以查看GPU的详细信息&#xff0c;以供各位同学们学习。 一、简单介绍 GPU-Z是一款方便实用的软件工具&#xff0c;专门为用户提供视频卡和GPU的详尽信息。它具有轻巧的特点&#xff0c;不需要安装即可使用&am…

2023版智慧高速智慧公路总体建设方案,售前人员必备方案

导读&#xff1a;原文《智慧高速智慧公路总体建设方案》共83页PPT&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 如需获取完整的电子版内容参考学习 您可以关注评论…

【雷达原理】基本雷达方程的推导

基本雷达方程 一、研究目的二、推导过程1、基本雷达方程常用的表达形式2、计算案例3、仿真代码 参考文献 一、研究目的 雷达方程定量地描述了作用距离与雷达参数及目标特性之间的关系。 研究雷达方程主要有以下作用&#xff1a; &#xff08;1&#xff09;根据雷达参数来估算雷…

慕课:笔记

课程链接&#xff1a;直面JavaScript中的30个疑难杂症_JavaScript面试题-慕课网 第二章&#xff1a;数据类型 数据类型是每门编程语言的必修之课&#xff0c;你是否对JavaScript的数据类型和检测存在困惑&#xff0c;本章节将为你揭晓其中的奥秘&#xff0c;让你对数据类型有…

矩阵压缩算法

当矩阵中存在着重复元素时&#xff0c;为了节省空间会采用压缩算法&#xff0c;关键在于原矩阵空间与压缩后数据结构的对应&#xff1b; 1.对称压缩&#xff1a;数据沿对角线对称的情况&#xff1b; 将矩阵压缩为一维数组&#xff0c;数组的长度是&#xff1a; 对于num[n][n…

VMware虚拟机暴露端口至公网方法流程详解

目录 需求背景 解决方法 准备工作 虚拟机ip设置方法 需求背景 一台电脑需要连接另一台电脑上的虚拟机的端口&#xff0c;直接ping是无法ping通的&#xff0c;因为本地虚拟机的端口未暴露至公网。 解决方法 虚拟机&#xff1a;CentOS 7 64 Linux 本机&#xff1a;Window…

C专家编程 —— 运行时数据结构

文章目录 代码和数据段代码与可执行文件中对应的位置可执行文件中的段在内存中的布局加入动态链接库的内存空间布局堆栈段的作用过程活动记录函数调用过程记录举例 static和auto关键字 汇编嵌入C代码 代码和数据 代码和数据的区别可以理解为编译时和运行时的分界线。 代码&…

guacamole 纯web rdp预研:相关JAVA基础

文章目录 guacamole 纯web rdp预研:相关JAVA基础1. pom.xml2 scm标签3 application/octet-stream4. tomcat webapps下war包5 maven-assembly-plugin maven assembly插件介绍什么是assembly&#xff1f; 6. Mavenz中的source插件的使用和注意事项。7. Maven私库安装与配置8. 配置…

深度学习之目标检测R-CNN模型算法流程详解说明(超详细理论篇)

1.R-CNN论文背景 2. R-CNN算法流程 3. R-CNN创新点 一、R-CNN论文背景 论文网址https://openaccess.thecvf.com/content_cvpr_2014/papers/Girshick_Rich_Feature_Hierarchies_2014_CVPR_paper.pdf   RCNN&#xff08;Region-based Convolutional Neural Networks&#xff…

牛客网基础语法81~90题

牛客网基础语法81~90题&#x1f618;&#x1f618;&#x1f618; &#x1f4ab;前言&#xff1a;今天是咱们第九期刷牛客网上的题目。 &#x1f4ab;目标&#xff1a;可以循环嵌套使用熟练&#xff0c;数组的变问题&#xff0c;对数学知识掌握更加清晰。 &#x1f4ab;鸡汤&…

Matplotlib---热力图

1. 热力图 imshow 是 Matplotlib 库中一个函数&#xff0c;主要用于在 Python 中显示图像。它的完整参数列表如下&#xff1a; matplotlib.pyplot.imshow(X, cmapNone, normNone, aspectNone, interpolationNone, alphaNone, vminNone, vmaxNone, originNone, extentNone, sh…

管理类联考——逻辑——知识篇——论证推理——三、假设——haimian

假设 考点分析 假设 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量1132111 主要问法 上述论述是基于以下哪项假设?基于以下哪项假设能使上述推理成立?上述论证依赖于以下哪项假设?得到这一结论的前提条件是? 解题思路 阅读问题&#xff0c;确…

【TCP/IP】多播 - 定义、原理及编程实现(TTL、多播组、收发信息)

目录 多播 多播的原理 多播的数据传输时的特点 TTL 的概念 TTL 和 多播组的配置方法 多播的编程与实现 发送者 接收者 多播 多播是一种介于单播和广播通信之间的技术方式&#xff0c;可以将发送者所需要发送的数据包分别发送给分散在不同子网中的一组接收者。 多播的原…

分布式软件架构——事务ACID

事务概念 事务处理几乎是每一个信息系统中都会涉及到的问题&#xff0c;它存在的意义就是保证系统中的数据是正确的&#xff0c;不同数据间不会产生矛盾&#xff0c;也就是保证数据状态的一致性&#xff08;Consistency&#xff09; 关于一致性&#xff0c;我们重点关注的是数…

ElasticSearch-安装Head可视化插件

安装Head可视化插件 首先需要依赖node.js和npm环境 1 安装node.js 官方下载地址:http://nodejs.cn/download/ 下载LTS版本&#xff08;长期稳定版本&#xff09; 安装可以更改安装路径,其余的都是选择 下一步傻瓜是安装 安装成功后如下 命令测试 node -v 查看node的版本 n…

理解redis的多线程和IO多路复用

参考资料 https://blog.csdn.net/TZ845195485/article/details/119745735 Redis单线程和多线程问题的背景 Redis里程碑版本迭代 Redis的单线程 主要是指Redis的网络IO和键值对读写是由一个线程来完成的&#xff0c;Redis在处理客户端的请求时包括获取&#xff08;socket读&a…