`
zhp8341
  • 浏览: 10580 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java8特性lambda基本原理及性能分析

阅读更多



原文:http://www.ccblog.cn/85.htm

 

Java8发布,Lambda表达式作为一项重要的特性随之而来。或许现在你已经在使用Lambda表达式来书写简洁灵活的代码。

Lambda 表达式是一种匿名函数(对 Java 而言这并不完全正确,但现在姑且这么认为),

简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。

你可以将其想做一种速记,在你需要使用某个方法的地方写上它。当某个方法只使用一次,而且定义很简短,使用这种速记替代之尤其有效,这样,你就不必在类中费力写声明与方法了。

 

函数式接口

 

函数式接口(functional interface)。简单来说,函数式接口是只包含一个方法的接口。比如Java标准库中的java.lang.Runnable和java.util.Comparator都是典型的函数式接口。

java 8提供 @FunctionalInterface作为注解,这个注解是非必须的,只要接口符合函数式接口的标准(即只包含一个方法的接口),

虚拟机会自动判断,但 最好在接口上使用注解@FunctionalInterface进行声明,以免团队的其他人员错误地往接口中添加新的方法。 

Java中的lambda无法单独出现,它需要一个函数式接口来盛放,lambda表达式方法体其实就是函数接口的实现,

 

Lambda语法

 

包含三个部分

 

一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数

一个箭头符号:->

方法体,可以是表达式和代码块,方法体函数式接口里面方法的实现,如果是代码块,则必须用{}来包裹起来,且需要一个return 返回值,但有个例外,若函数式接口里面方法返回值是void,则无需{}

 

(parameters) -> expression 或者 (parameters) -> { statements; }

 

 

我们看个线程的demo

 

public class TestLambda {
    
    //lambda实现
    public static void runThreadUseLambda() {
        new Thread(() ->System.out.println("我是lambda实现的线程")).start(); 
    }
    
    //内部类实现 
    public static void runThreadUseInnerClass() {
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("内部类实现的线程");
            }
        }).start();
    }
    public static void main(String[] args) {
        TestLambda.runThreadUseLambda();
        TestLambda.runThreadUseInnerClass();
    }
}

 

Lambda原理

 

以上是通过不同的方法来实现线程的,那么我们来看下JVM编译执行过程的原理是不是一样的?

通过 javap -c TestLambda查看字节码 

 

 

 

通过上面的对比发现红色部分不太一样Lambda是InvokeDynamic 内部类是 invokespecial,那么是什么意思呢?

我们先温习下jvm指令

invokeinterface:调用接口方法;

invokespecial:专门用来调用父类方法、私有方法和初始化方法;

invokestatic:调用静态方法;

invokevirtual:调用对象的一般方法。

这四个指令所对应的类、调用的方法在编译时几乎是固定的:invokestatic所对应的类为静态方法所在的类,方法为静态方法本身;invokespecial所对应的类为当前对象,方法是固定的;invokeinterface和invokevirtual所对应的类也为当前对象,方法可以因为继承和实现进行选择,但也仅限于整个继承体系中选择。

 

在java7 JVM中增加了一个新的指令invokedynamic,用于支持动态语言,即允许方法调用可以在运行时指定类和方法,不必在编译的时候确定。

字节码中每条invokedynamic指令出现的位置称为一个动态调用点,invokedynamic指令后面会跟一个指向常量池的调用点限定符,这个限定符会被解析为一个动态调用点。

 

我们发现Lambda采用的是invokedynamic指令,所以Lambda还是有别于普通方式的调用的。

 

 

 

我们在来看下List中Lambda的使用。

代码

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class TestLambdaList {
    public static void main(String[] args) {
        List userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i);
            user.setUserName("un" + i);
            user.setPassword("password" + i);
            userList.add(user);
        }
        // 通过Lambda取出User里面id的值
        List idList_LB = userList.stream().map(user -> user.getId()).collect(Collectors.toList());
        System.out.println(idList_LB);
        // 普通方法
        List idList_PT = new ArrayList<>();
        for (User user : userList) {
            idList_PT.add(user.getId());
        }
        System.out.println(idList_PT);
    }
}
class User {
    Integer id;
    String  userName;
    String  password;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

 

以上是通过两种不同的方式实现取(出User里面id的值),很明显使用Lambda要简洁很多。

 

 

Lambda性能如何?

 

有许许多多关于 Java 8 中流效率的讨论,但根据 Alex Zhitnitsky 的测试结果显示:坚持使用传统的 Java 编程风格——iterator 和 for-each 循环——比 Java 8 的实现性能更佳。他们测出的结果是如下图

 

他们的结论:Java 8 中提供的任何一种新方式都会产生约 5 倍的性能差异。有时使用简单迭代器循环比混合 lambda 表达式和流更有效,即便这样需要多写几行代码,且需要跳过甜蜜的语法糖(syntactic suger)。

使用迭代器或 for-each 循环是遍历 ArrayList 最有效的方式,性能比采用索引值的传统 for 循环方式好两倍。

在 Java 8 的方法中,并行流的性能最佳。但是请小心,在某些情况下它也可能会导致程序运行得更慢。

 

我们自己来亲自测试下到底怎么样

将上面的代码添加个时间看下耗时情况

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class TestLambdaList {
    public static void main(String[] args) {
        List userList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            User user = new User();
            user.setId(i);
            user.setUserName("un" + i);
            user.setPassword("password" + i);
            userList.add(user);
        }
        // 通过Lambda取出User里面id的值
        long t1=System.currentTimeMillis();
        List idList_LB = userList.stream().map(user -> user.getId()).collect(Collectors.toList());
        //System.out.println(idList_LB);
        System.out.println(System.currentTimeMillis()-t1);
        // 普通方法
        long t2=System.currentTimeMillis();
        List idList_PT = new ArrayList<>();
        for (User user : userList) {
            idList_PT.add(user.getId());
        }
        System.out.println(System.currentTimeMillis()-t2);
       // System.out.println(idList_PT);
    }
}
class User {
    Integer id;
    String  userName;
    String  password;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

 

测试1:在一个Main方法里面执行完
i=100     多次测试的结果是:Lambda耗时在120左右。但是foreach每次的结果都是0;
i=100000   多次测试的结果是:Lambda耗时在120左右。但是foreach每次的结果都是10秒以内;
i=1000000  多次测试的结果是:Lambda耗时在120左右。但是foreach每次的结果都是10秒以内;  
i=10000000  多次测试的结果是:Lambda耗时在120左右。但是foreach每次的结果都是130秒以内; 


测试2:分别在一个Main方法里面执行完各自执行,(将其中的一个注释掉)
i=100     多次测试的结果是:Lambda耗时在100左右。但是foreach每次的结果都是0;
i=100000   多次测试的结果是:Lambda耗时在100-120左右。但是foreach每次的结果都是12-16秒以内;
i=1000000 多次测试的结果是:Lambda耗时在120~180左右。但是foreach每次的结果都是30-50秒以内;  
i=10000000  多次测试的结果是:Lambda耗时在120~180左右。但是foreach每次的结果都是40~60秒以内;

 

由此可见:使用简单迭代器循环比混合 lambda 表达式和流更有效.

 

总结:

 

开始使用 Java 8 的第一件事情是在实践中使用 lambda 表达式和流。但是请记住:它确实非常好,好到可能会让你上瘾!但是,我们也看到了,使用传统迭代器和 for-each 循环的 Java 编程风格比 Java 8 中的新方式性能高很多

当然,这也不是绝对的。但这确实是一个相当常见的例子,它显示可能会有大约 5 倍的性能差距。如果这影响到系统的核心功能或成为系统一个新的瓶颈,那就相当可怕了。

 

 

相关资料:https://jaxenter.com/how-java-8-lambdas-and-streams-can-make-your-code-5-times-slower-122603.html

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    Apress - Java Closures and Lambda.2015

    本书《Apress - Java Closures and Lambda》聚焦于Java 8中的新特性——闭包(Closures)和Lambda表达式。这些新功能不仅为Java语言带来了函数式编程的能力,还极大地提高了代码的可读性和维护性。本书通过深入浅出...

    Java高级特性之Lambda表达式:功能介绍、实战应用与常见问题解决

    内容概要:本文详细介绍了Java 8引入的Lambda表达式及其相关概念,如函数式接口、方法引用、流API等。文章首先回顾了Lambda表达式的背景和发展历程,接着详细讲解了Lambda表达式的语法结构和基本使用方法,以及如何...

    Java 性能分析

    使用Java 8的新特性,如流API和Lambda表达式,也能帮助简化代码并提高性能。 2. **数据结构与算法**:选择合适的数据结构(如HashMap、ArrayList、LinkedList)和算法(如排序、查找)对性能有很大影响。根据实际...

    Java虚拟机:JVM高级特性与最佳实践(第2版)

    7. **最新特性**:第二版可能涵盖了Java新版本中的JVM相关更新,比如Java 8的Lambda表达式和Stream API在JVM上的实现,或者Java 11的模块系统对JVM的影响。 通过阅读这本书,开发者不仅能够掌握JVM的内部运作,还能...

    java8 官方文档.

    Java 8官方文档详细解释了这些特性的使用和原理,同时还包含了API参考、教程、示例代码等内容,是Java开发者必备的学习资料。通过深入学习和实践,开发者能够更好地利用Java 8的特性来提高开发效率和软件质量。

    基于java8新特性+反射机制实现list不同实体类互转.zip

    在Java编程中,集合框架是核心...通过结合Java 8的新特性(如流API)和反射机制,我们能够在不牺牲性能的前提下,优雅地解决实体类列表互转的问题。在实际项目中,这样的工具类可以大大简化代码逻辑,提升开发效率。

    4本高清中文版Java性能优化经典书籍

    这本书深入探讨了Java 8的新特性,尤其是对于性能优化有重大影响的部分,如Lambda表达式、Stream API和并行流。Lambda表达式使得代码更加简洁,同时提高了函数式编程的能力,降低了回调函数的复杂性。Stream API则...

    51CTO学院-跟老谭学Java8系列视频教程第三季Java8高级语言特性.docx

    这一季不仅深入讲解了Java 8新增加的Lambda表达式等特性,还覆盖了一系列重要的Java编程概念和技术,如异常处理、泛型、反射、AOP(面向切面编程)、枚举、注解、线程管理和垃圾回收机制等。通过这些课程的学习,...

    java jdk8u151

    此版本是JDK 8的更新版本,主要包含了各种性能优化、安全修复以及对现有特性的增强。对于Java开发者来说,理解和掌握这个版本的关键特性至关重要。 1. **JDK 8的基础**: JDK 8是Java历史上的一个里程碑,它引入了...

    Java JDK 8u321 for Windows 64位 安装程序

    Java 8是Java平台的重大里程碑,引入了许多新特性,例如Lambda表达式、Stream API、方法引用、日期/时间API等,极大地提升了开发效率和代码可读性。 1. **安装过程**: - 下载:访问Oracle官方网站,找到Java SE ...

    Java 8 in Action

    - **并行流优化**: 介绍并行流的基本原理及其在大数据处理中的优势,并通过具体场景展示如何利用并行流来提高程序性能。 #### 四、总结 《Java 8 in Action》是一本内容丰富、实用性强的技术书籍,不仅详细介绍了...

    JAVA8 完整源码(包含Sun包源码)

    1. **Lambda 表达式**:Java 8 最显著的新特性之一就是 Lambda 表达式,它简化了函数式编程。在 `java.util.function` 包中,你可以看到各种函数接口,如 `Function,R&gt;`、`Predicate&lt;T&gt;` 和 `Consumer&lt;T&gt;`,它们使得...

    最新版javajdk8文档

    1. **Lambda表达式**:Java 8引入了Lambda表达式,这是一种简洁的函数式编程特性。它允许将匿名函数作为方法参数,或者用作方法返回值。Lambda表达式可以极大地简化那些只需要一次性的、短小的代码块的情况,如事件...

    JAVA核心技术 卷II:高级特性(原书第8版).pdf

    《JAVA核心技术 卷II:高级特性(原书第8版)》是一本深入探讨Java编程高级特性的权威著作。本书全面覆盖了Java平台标准版(Java SE)的关键技术和概念,旨在帮助开发者提升对Java编程语言的深层理解,掌握更高效、...

    Java in easy steps Covers Java 9 6th Edition

    Java是一种广泛使用的面向对象编程语言,它从Java 8版本开始引入了Lambda表达式和流(Streams),从而增强了Java的函数式编程特性。《Java in easy steps》涵盖了Java 9,并特别针对Java 8的新特性进行了详细讲解。...

    Java8语言和虚拟机规范

    这些规范不仅详尽阐述了Java 8的语法特性和JVM的内部工作原理,也为开发者提供了实现高性能、可维护的Java应用程序的指导原则。通过深入学习和理解这些规范,开发者能够更好地掌握Java 8的核心特性,编写出更高效、...

    Java核心技术(卷2):高级特性(原书第9版)

    9. **Lambda表达式和函数式编程**:Java 8引入了lambda表达式,简化了匿名函数的编写,使得函数式编程风格在Java中成为可能。`Stream API`是函数式编程的重要工具,它支持数据流的并行处理。 10. **Java虚拟机(JVM...

    Java核心技术 卷Ⅱ高级特性(原书第8版) 高清版

    9. **Lambda表达式与函数式编程**:Java 8引入了lambda表达式,使得函数式编程风格在Java中变得可能。这部分会讲述如何使用lambda简化代码,以及Stream API的应用。 10. **模块化系统(Jigsaw Project)**:Java 9...

Global site tag (gtag.js) - Google Analytics