从初学者到专家:Java的Lambda表达式完整指南

一.Lambda的概念

概念:Lambda表达式是Java 8引入的一项重要功能,它允许我们以更简洁和灵活的方式编写代码。可以把Lambda表达式看作是一种更方便的匿名函数,可以像数据一样传递和使用。

使用Lambda表达式可以让我们写出更短、更易读的代码。它可以替代传统的匿名类,使代码更加简洁。Lambda表达式还支持函数式编程,这意味着我们可以将函数作为参数传递给其他方法,使得代码更加灵活和可扩展。

1.1 Lambda表达式的语法

基本语法: (parameters) -> expression (parameters) ->{ statements; }
Lambda表达式由三部分组成:
  1. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
  2. ->:可理解为被用于的意思
  3. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。

根据上面的语法,理解下面的代码:

  1. 对于只有单个表达式的Lambda表达式: 
import java.util.Arrays;
import java.util.List;
public class LambdaExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// Lambda表达式作为参数传递给forEach方法numbers.forEach(number -> System.out.print(number+" "));}
}

运行截图如下:

这个示例首先创建了一个整数列表 numbers。然后,通过调用 forEach 方法并传递一个 Lambda 表达式作为参数,对列表中的每个元素执行操作。

2.对于包含多个语句的Lambda表达式:

import java.util.Arrays;
import java.util.List;
public class LambdaExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// Lambda表达式使用多个语句块numbers.forEach(number -> {int doubled = number * 2;System.out.println(number + " doubled: " + doubled);});}
}

运行截图:

Lambda 表达式使用了一个语句块,首先计算每个数字的两倍值,并打印原始数字和计算结果。


1.2 函数式接口

要了解 Lambda 表达式 , 首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法 。
注意:
  1. 如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
  2.  如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。

举个简单的例子:假设我是一位厨师,需要有一位助手来帮我。你给助手提供了一个简单的任务:切洋葱。你告诉助手只需要进行切洋葱的操作,其他的工作你会负责。 

在这个例子中,我们可以将这个任务看作是一个接口,而助手则是接口的实现者。这个接口定义了一个方法,即切洋葱的操作。

代码案例:

// 定义一个函数式接口
@FunctionalInterface
interface Task {
//注意只能有一个方法void perform();
}public class LambdaExample {public static void main(String[] args) {// 创建一个助手对象,使用Lambda表达式实现任务Task assistant = () -> System.out.println("助手正在切洋葱...");// 调用厨师的方法,传递助手对象执行任务cookMeal(assistant);}public static void cookMeal(Task task) {// 准备食材System.out.println("准备食材...");// 执行任务task.perform();// 煮菜System.out.println("开始烹饪...");}
}

运行截图:


如果我在接口再定义一个方法,则会报错。

但是有另外一种情况可以:

 在Java 8之前,接口中只能包含抽象方法,也就是没有具体的实现。但是,Java 8引入了默认方法的概念,允许在接口中定义具有默认实现的方法。默认方法使用default关键字进行修饰。

由于接口中的默认方法拥有具体的实现,所以你可以直接在接口中调用它们。在实现该接口的类中,可以选择是否覆盖默认方法,如果没有覆盖,默认方法会被继承并直接使用。

现在我在接口定义一个washVegetables()的默认方法。

package demo1;// 定义一个函数式接口
@FunctionalInterface
interface Task {void perform();default void washVegetables() {System.out.println("助理2,帮我洗菜即可");}
}public class Chef {public static void main(String[] args) {// 创建一个助手对象,使用Lambda表达式实现任务Task assistant1 = () -> {System.out.println("助手1正在切洋葱...");Task assistant2 = new Task() {@Overridepublic void perform() {washVegetables();}};assistant2.perform();};// 调用厨师的方法,传递助手对象执行任务cookMeal(assistant1);}public static void cookMeal(Task task) {// 准备食材prepareIngredients();// 执行任务task.perform();// 煮菜startCooking();}public static void prepareIngredients() {System.out.println("准备食材...");}public static void startCooking() {System.out.println("开始烹饪...");}
}

我们将助理1的任务修改为先切洋葱,然后在切洋葱完成后创建一个新的助理2对象,该对象通过实现Task接口并重写perform方法来调用washVegetables默认方法。然后,我们调用助理2的perform方法来执行洗菜操作。


二. Lambda表达式的基本使用

2.1函数接口的六种情况

首先,我们实现准备好几个接口:
//无返回值无参数
@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);
}
语法精简:
1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
2. 参数的小括号里面只有一个参数,那么小括号可以省略
3. 如果方法体当中只有一句代码,那么大括号可以省略
4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。
1.无返回值无参数的函数式接口
@FunctionalInterface
interface NoParameterNoReturn {void test();
}public class TestDemo {public static void main(String[] args) {// 无参数无返回值的函数式接口NoParameterNoReturn noParameterNoReturn = () -> {System.out.println("无参数无返回值");};noParameterNoReturn.test();}}

运行截图:


 2.一个参数无返回值的函数式接口

@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}
public class TestDemo {public static void main(String[] args) {OneParameterNoReturn oneParameterNoReturn = (int a) -> {System.out.println("一个参数无返回值:" + a);};oneParameterNoReturn.test(10);}

运行截图:


3.多个参数无返回值的函数式接口

@FunctionalInterface
interface MoreParameterNoReturn {void test(int a, int b);
}
public class TestDemo {public static void main(String[] args) {// 多个参数无返回值的函数式接口MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {System.out.println("多个参数无返回值:" + a + " " + b);};moreParameterNoReturn.test(20, 30);}

运行截图:


 4.有返回值无参数的函数式接口

@FunctionalInterface
interface NoParameterReturn {int test();
}
public class TestDemo {public static void main(String[] args) {NoParameterReturn noParameterReturn = () -> {System.out.println("有返回值无参数!");return 40;};int ret = noParameterReturn.test();System.out.println(ret);}

运行截图:


5.有返回值一个参数的函数式接口 

@FunctionalInterface
interface OneParameterReturn {int test(int a);
}
public class TestDemo {public static void main(String[] args) {OneParameterReturn oneParameterReturn = (int a) -> {System.out.println("有返回值有一个参数!");return a;};int ret = oneParameterReturn.test(50);System.out.println(ret);}

运行截图:


6.有返回值多个参数的函数式接口 

@FunctionalInterface
interface MoreParameterReturn {int test(int a, int b);
}
public class TestDemo {public static void main(String[] args) {MoreParameterReturn moreParameterReturn = (int a, int b) -> {System.out.println("有返回值多个参数!");return a + b;};int ret = moreParameterReturn.test(60, 70);System.out.println(ret);}

运行截图:

 2.2匿名内部类变量捕获

 Lambda 表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解 Lambda 表达式的作用域 。 Java 当中的匿名类中,会存在变量捕获。

 什么是匿名内部类?

匿名内部类就是没有名字的内部类 。我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用就好,那么下面我们来,简单的看看匿名内部类的使用就好了。

代码案例一

interface MyFunction {void printValue();
}public class Example {public static void main(String[] args) {int x = 10; // 外部作用域的变量MyFunction myFunction = new MyFunction() {@Overridepublic void printValue() {// 引用外部作用域的变量xSystem.out.println("x: " + x);}};x = 20; // 修改外部作用域的变量xmyFunction.printValue(); // 输出捕获的变量x,结果为20}
}

我们定义了一个函数式接口MyFunction,其中包含了一个抽象方法printValue()。然后,我们创建了一个匿名内部类实现了该接口,并在实现中引用了外部作用域中的变量x,并打印出其值。 


代码案例二 

interface Shape {void draw();
}public class Example {public static void main(String[] args) {final int x = 10; // 外部作用域的变量Shape shape = new Shape() {@Overridepublic void draw() {System.out.println("Drawing a shape with x = " + x);}};shape.draw(); // 使用匿名内部类重写的draw()方法进行绘制}
}

我们定义了一个Shape接口,其中包含了一个抽象方法draw()。然后,我们使用匿名内部类实现了该接口,并在实现中引用了外部作用域中的变量x。在draw()方法中,我们打印出了变量x的值。

 2.3Lambda的变量捕获

Lambda表达式可以捕获外部作用域的变量,这使得Lambda表达式可以访问和操作外部作用域中的变量。捕获的变量在Lambda表达式中被视为"有效final",即虽然没有显式声明为final,但它们在Lambda表达式中不能被修改。
 

代码案例:

@FunctionalInterface
interface NoParameterNoReturn {void test();
}public class TestDemo {@FunctionalInterfaceinterface NoParameterNoReturn {void test();}public static void main(String[] args) {int a = 10;NoParameterNoReturn noParameterNoReturn = ()->{System.out.println("捕获变量:"+a);};noParameterNoReturn.test();}}

运行截图:

现在我要修改变量a =99


三.Lambda在集合当中的使用

为了能够让 Lambda Java 的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与 Lambda 表达式对接。
以下是对应接口的常用方法及其使用:

 3.1Collection接口

forEach()方法

使用 forEach() 方法可以方便地遍历集合中的元素,并对每个元素执行自定义操作,从而简化了对集合的处理过程。

List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");fruits.forEach(fruit -> System.out.println("I like " + fruit));// 输出结果:
// I like Apple
// I like Banana
// I like Orange

removeIf() 方法

removeIf(Predicate<? super E> filter):使用Lambda表达式来移除集合中满足特定条件的元素。Predicate接口的Lambda表达式用于定义过滤条件。
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));numbers.removeIf(n -> n % 2 == 0); // 移除所有偶数// 输出结果:[1, 3, 5]
System.out.println(numbers);

spliterator() 方法

返回一个可用于并行迭代集合的Spliterator对象。Spliterator接口的forEachRemaining()方法可以与Lambda表达式一起使用,对集合中的每个元素执行特定操作。

List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");Spliterator<String> spliterator = fruits.spliterator();
spliterator.forEachRemaining(fruit -> System.out.println(fruit));// 输出结果:
// Apple
// Banana
// Orange


stream()方法

返回一个顺序流,用于对集合中的元素进行顺序操作。可以与forEach()方法结合使用,对集合中的每个元素执行特定操作。

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");names.stream().forEach(name -> System.out.println("Hello, " + name));// 输出结果:
// Hello, Alice
// Hello, Bob
// Hello, Charlie

parallelStream() 方法

 返回一个并行流,用于对集合中的元素进行并行操作。可以与forEach()方法结合使用,对集合中的每个元素执行特定操作。
 

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");names.parallelStream().forEach(name -> System.out.println("Hello, " + name));// 输出结果:
// Hello, Alice
// Hello, Bob
// Hello, Charlie

 3.2List接口

replaceAll()方法

使用Lambda表达式替换列表中的所有元素。

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));numbers.replaceAll(n -> n * 2); // 将列表中的每个元素乘以2// 输出结果:[2, 4, 6, 8, 10]
System.out.println(numbers);

sort()方法

使用Lambda表达式对列表进行排序。Comparator接口的Lambda表达式用于定义排序逻辑。

List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));names.sort((name1, name2) -> name1.compareToIgnoreCase(name2)); // 根据名称的字母顺序排序,忽略大小写// 输出结果:[Alice, Bob, Charlie]
System.out.println(names);

3.3Map接口

forEach()方法

使用Lambda表达式对Map中的每个键值对执行特定的操作。BiConsumer接口的Lambda表达式用于定义操作逻辑,接受键和值作为参数。

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 80);
scores.put("Charlie", 95);scores.forEach((name, score) -> System.out.println(name + ": " + score));// 输出结果:
// Alice: 90
// Bob: 80
// Charlie: 95

replaceAll()方法

使用Lambda表达式替换Map中的所有值。

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 80);
scores.put("Charlie", 95);scores.replaceAll((name, score) -> score + 5); // 将每个分数加上5System.out.println(scores);// 输出结果:
// {Alice=95, Bob=85, Charlie=100}


putIfAbsent()方法

使用Lambda表达式在Map中插入键值对,仅当键不存在时才插入。Lambda表达式用于定义要插入的值,接受键作为参数。

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 80);scores.putIfAbsent("Charlie", 95); // 插入键值对"Charlie=95"System.out.println(scores);// 输出结果:
// {Alice=90, Bob=80, Charlie=95}

remove()方法

使用Lambda表达式根据键和值从Map中移除指定的键值对。Lambda表达式用于定义要移除的值,接受键和当前值作为参数。

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 80);
scores.put("Charlie", 95);scores.remove("Alice", 90); // 移除键值对"Alice=90"System.out.println(scores);// 输出结果:
// {Bob=80, Charlie=95}


replace()方法

使用Lambda表达式替换Map中指定键的值。Lambda表达式用于定义要替换的值,接受键和当前值作为参数。

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 80);
scores.put("Charlie", 95);scores.replace("Alice", 100); // 将键"Alice"的值替换为100System.out.println(scores);// 输出结果:
// {Alice=100, Bob=80, Charlie=95}

四.总结 

Lambda 表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:
  • 代码简洁,开发迅速
  • 方便函数式编程
  • 非常容易进行并行计算
  • Java 引入 Lambda,改善了集合操作
缺点:
  • 代码可读性变差
  • 在非并行计算中,很多计算未必有传统的 for 性能要高
  • 不容易进行调试

      

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

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

相关文章

C++ —— 内存管理

目录 1. C内存分布 2. C 内存管理方式 2.1 new 和 delete 操作内置类型 2.2 new 和 delete 操作自定义类型 3. operator new与operator delete函数 4. new和delete的实现原理 5. malloc/free 和 new/delete 的区别 1. C内存分布 首先看一段代码&#xff1a; int globalV…

用Python的turtle库绘制皮卡丘

turtle库的简介 turtle(海龟)库是turtle绘图体系的python实现&#xff0c;turtle库是一种标准库&#xff0c;是python自带的。 turtle(海龟)是一种真实的存在&#xff0c;有一个海龟在窗口的正中心&#xff0c;在画布上游走&#xff0c;走过的轨迹形成了绘制的图形&#xff0…

进阶二叉树

目录 二叉树 二叉搜索树 二叉搜索树的定义 二叉搜索树的操作 哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 哈夫曼树的性质 平衡二叉树 平衡二叉树的定义&#xff1a; 平衡二叉树的插入调整 1.LL插入/LL旋转 2.RR插入/RR旋转 3.LR插入/LR旋转 4.RL插入/RL旋转 二叉树…

仿牛客项目Day10——统一异常处理、记录日志

统一异常处理 在controller里创建advice包&#xff0c;创建ExceptionAdvice类 这个注解括号里面是指只扫描被Controller标注的bean 请求request、响应response、异常exception 普通请求和异步请求的区别在于传的是json还是html吗&#xff1f; 统一记录日志 面向切面编程&…

IO多路复用、域套接字

思维导图 面试题TCP通信中的三次握手和四次&#xff1a; 客户端像向服务器端发送连接请求 服务器应答连接请求 客户端与服务器简历连接 客户端向服务器发送断开请求 服务器应答断开请求 服务器请求关闭连接 客户端发送确认应答 并行和并发的区别&#xff1a; 并行&#xff1a…

外包干了6天,技术明显进步。。。

我是一名大专生&#xff0c;自19年通过校招进入湖南某软件公司以来&#xff0c;便扎根于功能测试岗位&#xff0c;一晃便是近四年的光阴。今年8月&#xff0c;我如梦初醒&#xff0c;意识到长时间待在舒适的环境中&#xff0c;已让我变得不思进取&#xff0c;技术停滞不前。更令…

R语言Meta分析核心技术:从入门到精通

R语言作为一种强大的统计分析和绘图语言&#xff0c;在科研领域发挥着日益重要的作用。其中&#xff0c;Meta分析作为一种整合多个独立研究结果的统计方法&#xff0c;在R语言中得到了广泛的应用。通过R语言进行Meta分析&#xff0c;研究者能够更为准确、全面地评估某一研究问题…

Java毕业设计-基于springboot开发的Java时间管理系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、管理员功能模块2、用户功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的Java时间管理系统-毕业论文答…

‘sc‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

问题描述&#xff1a; sc 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 通过mysql黑窗口命令 “sc delete 服务名” 删除mysql服务时系统报错&#xff1a;sc不是内部命令或外部命令 原因&#xff1a; 系统默认环境变量值发生变化&#xff1b; 解决&…

从零开始学习typescript系列6: typescript各种类型以及类型特殊使用

基础类型的分类 常用 boolean: 布尔值number: 支持2/8/10/16进制string: 字符串enum: 枚举类型&#xff0c;可根据value找到keyarray: 普通数组&#xff0c;有2种方式&#xff0c;string[]或者 Array<string>tuple: 特殊数组&#xff0c;指定数组里的每个元素的类型&am…

UE snap02 解析ASCII文本文件

UE snap02 解析ASCII文本文件 示例数据data.dat 11389477.2714892 3364559.73645693 0 11389471.5162524 3364567.8860295 0 11389471.5162524 3365813.09618369 0 11388329.6082659 3366184.85895869 0 11388320.4775297 3366197.78833087 0 11388270.6882384 3366214.84811…

爬虫实战-Python爬取百度当天热搜内容

爬虫实战-Python爬取百度当天热搜内容 学习建议学习目标预期内容目标分解热搜地址热搜标题热搜简介热搜指数小总结 代码实现总结 学习建议 本文仅用于学习使用&#xff0c;不做他用&#xff1b;本文仅获取页面的内容&#xff0c;作为学习和对Python知识的了解&#xff0c;不会…

2040X系列 电子校准件

苏/州/新/利/通 2040X系列 电子校准件 300kHz&#xff5e;67GHz 简述 2040X系列电子校准件包括20402/20403/20404/20405/20409五种型号&#xff0c;覆盖频段300kHz&#xff5e;18GHz/10MHz&#xff5e;26.5GHz/10MHz&#xff5e;50GHz/10MHz&#xff5e;20GHz/10MHz&#xf…

redis学习-Set集合类型相关命令及特殊情况分析

目录 1. sadd key value1 value2 ... 2. smembers key 3. sismember key value 4. scard key 5. srem key value1 value2 ... 6. srandmember key num 7. spop key num 8. smove key1 key2 value 9. sdiff key1 key2 key3 ... 10. sinter key1 key2 ... 11. sunion key1 key2 .…

鸿蒙一次开发,多端部署(二)从一个例子开始

本章通过一个天气应用&#xff0c;介绍一多应用的整体开发过程&#xff0c;包括UX设计、工程管理及调试、页面开发等。 UX设计 本示例中的天气应用包含主页、管理城市和添加城市三个页面&#xff0c;其中主页中又包含菜单和更新间隔两个弹窗&#xff0c;基本业务逻辑如下所示…

Aztec的客户端证明

1. 引言 隐私保护 zk-rollup 的证明生成与通用 zk-rollup 的证明生成有很大不同。原因是给定交易中存在特定数据&#xff08;由私有函数处理&#xff09;&#xff0c;我们希望保持完全私有。在本文中&#xff0c;我们探讨了用于证明私有函数正确执行的客户端证明生成&#xff…

arm-linux实现onvif server+WS-UsernameToken令牌验证

目录 一、环境搭建 1、安装openssl 2、安装bison 3、安装flex 二、gsoap下载 三、编译x86版本gsoap 四、编译arm-linux版本gsoap 1、交叉编译openssl 1.1、下载openssl 1.2、交叉编译 2、交叉编译zlib 2.1、下载zlib 2.2、交叉编译 3、交叉编译gsoap 3.1、编译过…

【嵌入式学习】Qtday03.21

一、思维导图 二、练习 自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面。&#xff08;不要使用课堂上的图片和代码&#xff0c;自己发挥&#xff0c;有利于后面项目的完成&#xff09; 要求&#xff1a; 1. 需要使用Ui界面文件进行界面设计 2. ui界面上的组件…

深入了解 Spring boot的事务管理机制:掌握 Spring 事务的几种传播行为、隔离级别和回滚机制,理解 AOP 在事务管理中的应用

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

LeetCode 热题 100 | 堆(一)

目录 1 什么是堆排序 1.1 什么是堆 1.2 如何构建堆 1.3 举例说明 2 215. 数组中的第 K 个最大元素 2.1 子树大根化 2.2 遍历所有子树 2.3 弹出栈顶元素 2.4 完整代码 菜鸟做题&#xff0c;语言是 C 1 什么是堆排序 1.1 什么是堆 堆的定义和分类&#xff…