Java8之lambda最佳实践_动力节点Java学院整理

发布时间 - 2026-01-11 01:35:56    点击率:

在8 里面Lambda是最火的主题,不仅仅是因为语法的改变,更重要的是带来了函数式编程的思想,我觉得优秀的程序员,有必要学习一下函数式编程的思想以开阔思路。所以这篇文章聊聊Lambda的应用场景,性能,也会提及下不好的一面。

Java为何需要Lambda

1996年1月,Java 1.0发布了,此后计算机编程领域发生了翻天覆地的变化。商业发展需要更复杂的应用,大多数程序都跑在更强大的装备多核CPU的机器上。带有高效运行期编译器的Java虚拟机(JVM)的出现,使得程序员将精力更多放在编写干净、易于维护的代码上,而不是思考如何将每一个CPU时钟、每一字节内存物尽其用。

多核CPU的出现成了“房间里的大象”,无法忽视却没人愿意正视。算法中引入锁不但容易出错,而且消耗时间。人们开发了java.util.concurrent包和很多第三方类库,试图将并发抽象化,用以帮助程序员写出在多核CPU上运行良好的程序。不幸的是,到目前为止,我们走得还不够远。

那些类库的开发者使用Java时,发现抽象的级别还不够。处理大数据就是个很好的例子,面对大数据,Java还欠缺高效的并行操作。Java 8允许开发者编写复杂的集合处理算法,只需要简单修改一个方法,就能让代码在多核CPU上高效运行。为了编写并行处理这些大数据的类库,需要在语言层面上修改现有的Java:增加lambda表达式。

当然,这样做是有代价的,程序员必须学习如何编写和阅读包含lambda表达式的代码,但是,这不是一桩赔本的买卖。与手写一大段复杂的、线程安全的代码相比,学习一点新语法和一些新习惯容易很多。开发企业级应用时,好的类库和框架极大地降低了开发时间和成本,也扫清了开发易用且高效的类库的障碍。

Lambda的应用场景

下面我将重点放在函数式编程的实用性上,包括那些可以被大多数程序员理解和使用的技术,我们关心的如何写出好代码,而不是符合函数编程风格的代码。

1.1.1 1.使用() -> {} 替代匿名类

现在Runnable线程,Swing,JavaFX的事件监听器代码等,在java 8中你可以使用Lambda表达式替代丑陋的匿名类。

//Before Java 8:
new Thread(new Runnable() {
 @Override
 public void run() {
  System.out.println("Before Java8 ");
 }
}).start();
//Java 8 way:
new Thread(() -> System.out.println("In Java8!"));
// Before Java 8:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent e) {
   System.out.println("without lambda expression is boring");
  }
  });
// Java 8 way:
show.addActionListener((e) -> {
 System.out.println("Action !! Lambda expressions Rocks");
});

使用内循环替代外循环

外循环:描述怎么干,代码里嵌套2个以上的for循环的都比较难读懂;只能顺序处理List中的元素;

内循环:描述要干什么,而不是怎么干;不一定需要顺序处理List中的元素

//Prior Java 8 :
List features = Arrays.asList("Lambdas", "Default Method", 
"Stream API", "Date and Time API");
for (String feature : features) {
 System.out.println(feature);
}
//In Java 8:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API",
 "Date and Time API");
features.forEach(n -> System.out.println(n));
// Even better use Method reference feature of Java 8
// method reference is denoted by :: (double colon) operator
// looks similar to score resolution operator of C++
features.forEach(System.out::println);

Output:
Lambdas
Default Method
Stream API
Date and Time API

支持函数编程

为了支持函数编程,Java 8加入了一个新的包java.util.function,其中有一个接口java.util.function.Predicate是支持Lambda函数编程:

public static void main(args[]){
 List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 System.out.println("Languages which starts with J :");
 filter(languages, (str)->str.startsWith("J"));
 System.out.println("Languages which ends with a ");
 filter(languages, (str)->str.endsWith("a"));
 System.out.println("Print all languages :");
 filter(languages, (str)->true);
 System.out.println("Print no language : ");
 filter(languages, (str)->false);
 System.out.println("Print language whose length greater than 4:");
 filter(languages, (str)->str.length() > 4);
}
 public static void filter(List names, Predicate condition) {
 names.stream().filter((name) -> (condition.test(name)))
  .forEach((name) -> {System.out.println(name + " ");
 });
 }
Output:
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell

处理数据?用管道的方式更加简洁

Java 8里面新增的Stream API ,让集合中的数据处理起来更加方便,性能更高,可读性更好

假设一个业务场景:对于20元以上的商品,进行9折处理,最后得到这些商品的折后价格。

final BigDecimal totalOfDiscountedPrices = prices.stream()
.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
.reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);

想象一下:如果用面向对象处理这些数据,需要多少行?多少次循环?需要声明多少个中间变量?

Lambda的阴暗面

前面都是讲Lambda如何改变Java程序员的思维习惯,但Lambda确实也带来了困惑

JVM可以执行任何语言编写的代码,只要它们能编译成字节码,字节码自身是充分OO的,被设计成接近于Java语言,这意味着Java被编译成的字节码非常容易被重新组装。

但是如果不是Java语言,差距将越来越大,Scala源码和被编译成的字节码之间巨大差距是一个证明,编译器加入了大量合成类 方法和变量,以便让JVM按照语言自身特定语法和流程控制执行。

我们首先看看Java 6/7中的一个传统方法案例:

// simple check against empty strings
public static int check(String s) {
 if (s.equals("")) {
  throw new IllegalArgumentException();
 }
 return s.length();
}
//map names to lengths
List lengths = new ArrayList();
for (String name : Arrays.asList(args)) {
 lengths.add(check(name));
}

如果一个空的字符串传入,这段代码将抛出错误,堆栈跟踪如下:

at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.main(LmbdaMain.java:34)

再看看Lambda的例子

Stream lengths = names.stream().map(name -> check(name));
 at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.lambda$0(LmbdaMain.java:37)
at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
at LmbdaMain.main(LmbdaMain.java:39)

 这非常类似Scala,出错栈信息太长,我们为代码的精简付出力代价,更精确的代码意味着更复杂的调试。
但这并不影响我们喜欢Lambda!

总结

在Java世界里面,面向对象还是主流思想,对于习惯了面向对象编程的开发者来说,抽象的概念并不陌生。面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,程序也是如此,因此这两种编程方式我们都得学。

这种新的抽象方式还有其他好处。很多人不总是在编写性能优先的代码,对于这些人来说,函数式编程带来的好处尤为明显。程序员能编写出更容易阅读的代码——这种代码更多地表达了业务逻辑,而不是从机制上如何实现。易读的代码也易于维护、更可靠、更不容易出错。

在写回调函数和事件处理器时,程序员不必再纠缠于匿名内部类的冗繁和可读性,函数式编程让事件处理系统变得更加简单。能将函数方便地传递也让编写惰性代码变得容易,只有在真正需要的时候,才初始化变量的值。
总而言之,Java更趋于完美了。


# java  # 8  # lambda  # Java8新特性之lambda(动力节点Java学院整理)  # 30分钟入门Java8之lambda表达式学习  # Java8 新特性Lambda表达式实例详解  # Java8中的 Lambda表达式教程  # Java8中lambda表达式的应用及一些泛型相关知识  # Java8 Lambda表达式详解及实例  # Java8新特性lambda表达式有什么用(用法实例)  # Java8新特性之Lambda表达式浅析  # Java8新特性之lambda的作用_动力节点Java学院整理  # 多核  # 类库  # 面向对象  # 的是  # 放在  # 而不  # 带来了  # 编译成  # 还不够  # 而不是  # 都是  # 是一个  # 是个  # 很好  # 是因为  # 成了  # 也会  # 物尽其用  # 我觉得  # 是有 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251811 】 【 AI营销90571


相关推荐: 电商网站制作价格怎么算,网上拍卖流程以及规则?  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  Python文件操作最佳实践_稳定性说明【指导】  打造顶配客厅影院,这份100寸电视推荐名单请查收  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧  Laravel如何处理CORS跨域请求?(配置示例)  Gemini怎么用新功能实时问答_Gemini实时问答使用【步骤】  Laravel定时任务怎么设置_Laravel Crontab调度器配置  如何在万网主机上快速搭建网站?  Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑  想要更高端的建设网站,这些原则一定要坚持!  宙斯浏览器文件分类查看教程 快速筛选视频文档与图片方法  如何在IIS中新建站点并配置端口与IP地址?  EditPlus 正则表达式 实战(3)  Linux系统运维自动化项目教程_Ansible批量管理实战  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  什么是javascript作用域_全局和局部作用域有什么区别?  Laravel如何实现用户密码重置功能?(完整流程代码)  Laravel Vite是做什么的_Laravel前端资源打包工具Vite配置与使用  如何在局域网内绑定自建网站域名?  JS经典正则表达式笔试题汇总  Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道  JavaScript数据类型有哪些_如何准确判断一个变量的类型  Claude怎样写约束型提示词_Claude约束提示词写法【教程】  谷歌浏览器下载文件时中断怎么办 Google Chrome下载管理修复  Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】  详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)  利用vue写todolist单页应用  Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南  Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】  ,怎么在广州志愿者网站注册?  Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程  实例解析Array和String方法  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  Laravel怎么实现验证码(Captcha)功能  Laravel如何发送系统通知?(Notification渠道示例)  七夕网站制作视频,七夕大促活动怎么报名?  企业网站制作这些问题要关注  Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧  网站制作企业,网站的banner和导航栏是指什么?  Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】  齐河建站公司:营销型网站建设与SEO优化双核驱动策略  专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?  Laravel如何使用Livewire构建动态组件?(入门代码)  活动邀请函制作网站有哪些,活动邀请函文案?  Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】  如何在服务器上三步完成建站并提升流量?  Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】