关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java 8中的Lambda表达式最佳实践

    Java 8中的Lambda表达式最佳实践

    2015-05-01 00:00:00 出处:字节技术
    分享

    Java 8已经推出一段时间了,越来越多开发人员选择升级JDK,这条热门动弹里面看出,JDK7最多,其次是6和8,这是好事!

    在Java 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的语法,可以看这里。

    Lambda的应用场景

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

    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.使用内循环替代外循环

    外循环:描述怎么干,代码里嵌套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

    3.支持函数编程

    为了支持函数编程,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

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

    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);

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

    关于Stream API的详细信息,可以查看我之前写的文章 。

    Lambda的性能

    Java 8里面lambda的最佳实践

    Oracle公司的性能工程师Sergey Kuksenko有一篇很好的性能比较的文档: JDK 8: Lambda Performance study, 详细而全面的比较了lambda表达式和匿名函数之间的性能差别。这里是视频。 16页讲到最差(capture)也和inner class一样, non-capture好的情况是inner class的5倍。

    lambda开发组也有一篇ppt, 其中也讲到了lambda的性能(包括capture和非capture的情况)。看起来lambda最差的情况性能内部类一样, 好的情况会更好。

    Java 8 Lambdas – they are fast, very fast也有篇文章 (需要翻墙),表明lambda表达式也一样快。

    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更趋于完美了。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    最全面的前端开发指南

    Windows7任务栏桌面下角的一些正在运行的图标不见了

    sql server快速删除记录方法

    SQL Server 7移动数据的6种方法

    SQL Server 2008的新压缩特性

    每个Java程序员必须知道的5个JVM命令行标志

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。