找回密码
 会员注册
查看: 24|回复: 0

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

[复制链接]

9

主题

0

回帖

28

积分

新手上路

积分
28
发表于 2024-9-10 18:43:37 | 显示全部楼层 |阅读模式
一.Lambda的概念概念:Lambda表达式是Java8引入的一项重要功能,它允许我们以更简洁和灵活的方式编写代码。可以把Lambda表达式看作是一种更方便的匿名函数,可以像数据一样传递和使用。使用Lambda表达式可以让我们写出更短、更易读的代码。它可以替代传统的匿名类,使代码更加简洁。Lambda表达式还支持函数式编程,这意味着我们可以将函数作为参数传递给其他方法,使得代码更加灵活和可扩展。1.1Lambda表达式的语法基本语法parameters)->expression或(parameters)->{statements;}Lambda表达式由三部分组成:paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。->:可理解为“被用于”的意思方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。根据上面的语法,理解下面的代码:对于只有单个表达式的Lambda表达式: importjava.util.Arrays;importjava.util.List;publicclassLambdaExample{publicstaticvoidmain(String[]args){Listnumbers=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表达式:importjava.util.Arrays;importjava.util.List;publicclassLambdaExample{publicstaticvoidmain(String[]args){Listnumbers=Arrays.asList(1,2,3,4,5,6,7,8,9,10);//Lambda表达式使用多个语句块numbers.forEach(number->{intdoubled=number*2;System.out.println(number+"doubled:"+doubled);});}}运行截图:Lambda表达式使用了一个语句块,首先计算每个数字的两倍值,并打印原始数字和计算结果。1.2函数式接口要了解Lambda表达式,首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法。注意:如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口 如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。举个简单的例子:假设我是一位厨师,需要有一位助手来帮我。你给助手提供了一个简单的任务:切洋葱。你告诉助手只需要进行切洋葱的操作,其他的工作你会负责。 在这个例子中,我们可以将这个任务看作是一个接口,而助手则是接口的实现者。这个接口定义了一个方法,即切洋葱的操作。代码案例://定义一个函数式接口@FunctionalInterfaceinterfaceTask{//注意只能有一个方法voidperform();}publicclassLambdaExample{publicstaticvoidmain(String[]args){//创建一个助手对象,使用Lambda表达式实现任务Taskassistant=()->System.out.println("助手正在切洋葱...");//调用厨师的方法,传递助手对象执行任务cookMeal(assistant);}publicstaticvoidcookMeal(Tasktask){//准备食材System.out.println("准备食材...");//执行任务task.perform();//煮菜System.out.println("开始烹饪...");}}运行截图:如果我在接口再定义一个方法,则会报错。但是有另外一种情况可以: 在Java8之前,接口中只能包含抽象方法,也就是没有具体的实现。但是,Java8引入了默认方法的概念,允许在接口中定义具有默认实现的方法。默认方法使用default关键字进行修饰。由于接口中的默认方法拥有具体的实现,所以你可以直接在接口中调用它们。在实现该接口的类中,可以选择是否覆盖默认方法,如果没有覆盖,默认方法会被继承并直接使用。现在我在接口定义一个washVegetables()的默认方法。packagedemo1;//定义一个函数式接口@FunctionalInterfaceinterfaceTask{voidperform();defaultvoidwashVegetables(){System.out.println("助理2,帮我洗菜即可");}}publicclassChef{publicstaticvoidmain(String[]args){//创建一个助手对象,使用Lambda表达式实现任务Taskassistant1=()->{System.out.println("助手1正在切洋葱...");Taskassistant2=newTask(){@Overridepublicvoidperform(){washVegetables();}};assistant2.perform();};//调用厨师的方法,传递助手对象执行任务cookMeal(assistant1);}publicstaticvoidcookMeal(Tasktask){//准备食材prepareIngredients();//执行任务task.perform();//煮菜startCooking();}publicstaticvoidprepareIngredients(){System.out.println("准备食材...");}publicstaticvoidstartCooking(){System.out.println("开始烹饪...");}}我们将助理1的任务修改为先切洋葱,然后在切洋葱完成后创建一个新的助理2对象,该对象通过实现Task接口并重写perform方法来调用washVegetables默认方法。然后,我们调用助理2的perform方法来执行洗菜操作。二. Lambda表达式的基本使用2.1函数接口的六种情况首先,我们实现准备好几个接口://无返回值无参数@FunctionalInterfaceinterfaceNoParameterNoReturn{voidtest();}//无返回值一个参数@FunctionalInterfaceinterfaceOneParameterNoReturn{voidtest(inta);}//无返回值多个参数@FunctionalInterfaceinterfaceMoreParameterNoReturn{voidtest(inta,intb);}//有返回值无参数@FunctionalInterfaceinterfaceNoParameterReturn{inttest();}//有返回值一个参数@FunctionalInterfaceinterfaceOneParameterReturn{inttest(inta);}//有返回值多参数@FunctionalInterfaceinterfaceMoreParameterReturn{inttest(inta,intb);}语法精简:1.参数类型可以省略,如果需要省略,每个参数的类型都要省略。2.参数的小括号里面只有一个参数,那么小括号可以省略3.如果方法体当中只有一句代码,那么大括号可以省略4.如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字。1.无返回值无参数的函数式接口@FunctionalInterfaceinterfaceNoParameterNoReturn{voidtest();}publicclassTestDemo{publicstaticvoidmain(String[]args){//无参数无返回值的函数式接口NoParameterNoReturnnoParameterNoReturn=()->{System.out.println("无参数无返回值");};noParameterNoReturn.test();}}运行截图: 2.一个参数无返回值的函数式接口@FunctionalInterfaceinterfaceOneParameterNoReturn{voidtest(inta);}publicclassTestDemo{publicstaticvoidmain(String[]args){OneParameterNoReturnoneParameterNoReturn=(inta)->{System.out.println("一个参数无返回值:"+a);};oneParameterNoReturn.test(10);}运行截图:3.多个参数无返回值的函数式接口@FunctionalInterfaceinterfaceMoreParameterNoReturn{voidtest(inta,intb);}publicclassTestDemo{publicstaticvoidmain(String[]args){//多个参数无返回值的函数式接口MoreParameterNoReturnmoreParameterNoReturn=(inta,intb)->{System.out.println("多个参数无返回值:"+a+""+b);};moreParameterNoReturn.test(20,30);}运行截图: 4.有返回值无参数的函数式接口@FunctionalInterfaceinterfaceNoParameterReturn{inttest();}publicclassTestDemo{publicstaticvoidmain(String[]args){NoParameterReturnnoParameterReturn=()->{System.out.println("有返回值无参数!");return40;};intret=noParameterReturn.test();System.out.println(ret);}运行截图:5.有返回值一个参数的函数式接口 @FunctionalInterfaceinterfaceOneParameterReturn{inttest(inta);}publicclassTestDemo{publicstaticvoidmain(String[]args){OneParameterReturnoneParameterReturn=(inta)->{System.out.println("有返回值有一个参数!");returna;};intret=oneParameterReturn.test(50);System.out.println(ret);}运行截图:6.有返回值多个参数的函数式接口 @FunctionalInterfaceinterfaceMoreParameterReturn{inttest(inta,intb);}publicclassTestDemo{publicstaticvoidmain(String[]args){MoreParameterReturnmoreParameterReturn=(inta,intb)->{System.out.println("有返回值多个参数!");returna+b;};intret=moreParameterReturn.test(60,70);System.out.println(ret);}运行截图: 2.2匿名内部类变量捕获 Lambda表达式中存在变量捕获,了解了变量捕获之后,我们才能更好的理解Lambda表达式的作用域。Java当中的匿名类中,会存在变量捕获。 什么是匿名内部类?匿名内部类就是没有名字的内部类。我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用就好,那么下面我们来,简单的看看匿名内部类的使用就好了。代码案例一interfaceMyFunction{voidprintValue();}publicclassExample{publicstaticvoidmain(String[]args){intx=10;//外部作用域的变量MyFunctionmyFunction=newMyFunction(){@OverridepublicvoidprintValue(){//引用外部作用域的变量xSystem.out.println("x:"+x);}};x=20;//修改外部作用域的变量xmyFunction.printValue();//输出捕获的变量x,结果为20}}我们定义了一个函数式接口MyFunction,其中包含了一个抽象方法printValue()。然后,我们创建了一个匿名内部类实现了该接口,并在实现中引用了外部作用域中的变量x,并打印出其值。 代码案例二 interfaceShape{voiddraw();}publicclassExample{publicstaticvoidmain(String[]args){finalintx=10;//外部作用域的变量Shapeshape=newShape(){@Overridepublicvoiddraw(){System.out.println("Drawingashapewithx="+x);}};shape.draw();//使用匿名内部类重写的draw()方法进行绘制}}我们定义了一个Shape接口,其中包含了一个抽象方法draw()。然后,我们使用匿名内部类实现了该接口,并在实现中引用了外部作用域中的变量x。在draw()方法中,我们打印出了变量x的值。 2.3Lambda的变量捕获Lambda表达式可以捕获外部作用域的变量,这使得Lambda表达式可以访问和操作外部作用域中的变量。捕获的变量在Lambda表达式中被视为"有效final",即虽然没有显式声明为final,但它们在Lambda表达式中不能被修改。 代码案例:@FunctionalInterfaceinterfaceNoParameterNoReturn{voidtest();}publicclassTestDemo{@FunctionalInterfaceinterfaceNoParameterNoReturn{voidtest();}publicstaticvoidmain(String[]args){inta=10;NoParameterNoReturnnoParameterNoReturn=()->{System.out.println("捕获变量:"+a);};noParameterNoReturn.test();}}运行截图:现在我要修改变量a=99则三.Lambda在集合当中的使用为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接。以下是对应接口的常用方法及其使用: 3.1Collection接口forEach()方法使用 forEach() 方法可以方便地遍历集合中的元素,并对每个元素执行自定义操作,从而简化了对集合的处理过程。Listfruits=Arrays.asList("Apple","Banana","Orange");fruits.forEach(fruit->System.out.println("Ilike"+fruit));//输出结果://IlikeApple//IlikeBanana//IlikeOrangeremoveIf()方法removeIf(Predicatefilter):使用Lambda表达式来移除集合中满足特定条件的元素。Predicate接口的Lambda表达式用于定义过滤条件。Listnumbers=newArrayList(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表达式一起使用,对集合中的每个元素执行特定操作。Listfruits=Arrays.asList("Apple","Banana","Orange");Spliteratorspliterator=fruits.spliterator();spliterator.forEachRemaining(fruit->System.out.println(fruit));//输出结果://Apple//Banana//Orangestream()方法返回一个顺序流,用于对集合中的元素进行顺序操作。可以与forEach()方法结合使用,对集合中的每个元素执行特定操作。Listnames=Arrays.asList("Alice","Bob","Charlie");names.stream().forEach(name->System.out.println("Hello,"+name));//输出结果://Hello,Alice//Hello,Bob//Hello,CharlieparallelStream()方法 返回一个并行流,用于对集合中的元素进行并行操作。可以与forEach()方法结合使用,对集合中的每个元素执行特定操作。 Listnames=Arrays.asList("Alice","Bob","Charlie");names.parallelStream().forEach(name->System.out.println("Hello,"+name));//输出结果://Hello,Alice//Hello,Bob//Hello,Charlie 3.2List接口replaceAll()方法使用Lambda表达式替换列表中的所有元素。Listnumbers=newArrayList(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表达式用于定义排序逻辑。Listnames=newArrayList(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表达式用于定义操作逻辑,接受键和值作为参数。Mapscores=newHashMap();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:95replaceAll()方法使用Lambda表达式替换Map中的所有值。Mapscores=newHashMap();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表达式用于定义要插入的值,接受键作为参数。Mapscores=newHashMap();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表达式用于定义要移除的值,接受键和当前值作为参数。Mapscores=newHashMap();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表达式用于定义要替换的值,接受键和当前值作为参数。Mapscores=newHashMap();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性能要高不容易进行调试
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-31 05:44 , Processed in 0.548423 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表