Lambda表达式为什么运行效率低

准备

我为什么说Lambda表达式运行效率低。

先准备一个list:

先用Lambda表达式的方式来循环一下这个list:

运行时间大概为110ms

再用普通方式来循环一下这个list:

运行时间大概为0ms或1ms

你们没看错,运行时间差别就是这么大,不相信的话大家可以自己去试一下,并且这并不是只有在循环时使用Lambda表达式才会导致运行效率低,而是Lambda表达式在运行时就是会需要额外的时间,我们继续来分析。

分析

如果我们要研究Lambda表达式,最正确、最直接的方法就是查看它所对应的字节码指令。

使用以下命令查看class文件对应的字节码指令:

上述命令解析出来的指令非常多,我这里提取比较重要的部分来给大家分析:

使用Lambda表达式所对应的字节码指令如下:

不使用Lambda表达式所对应的字节码指令如下:

从上面两种方式所对应的字节码指令可以看出,两种方式的执行方式确实不太一样。

不使用Lambda表达式执行循环流程

字节码指令执行步骤:

82:invokestatic:     执行静态方法,java/lang/System.currentTimeMillis:();

85-92:                      简单来说就是初始化数据,int i = 0;

93:invokeinterface:执行接口方法,接口为List,所以真正执行的是就是ArrayList.size方法;

98:if_icmpge:         比较,相当于执行i < list.size();

101:iinc:                 i++;

104:goto:               进行下一次循环;

107:invokestatic:   执行静态方法;

那么这个流程大家应该问题不大,是一个很正常的循环逻辑。

使用Lambda表达式执行循环流程

我们再来看一下对应的字节码指令:

字节码指令执行步骤:

34: invokestatic:        执行静态方法,java/lang/System.currentTimeMillis:();

37-38:                        初始化数据

39: invokedynamic:   这是在干什么?

44: invokeinterface:   执行java/util/List.forEach()方法

49: invokestatic:        执行静态方法,java/lang/System.currentTimeMillis:();

和上面正常循环的方式的字节码指令不太一样,我们认真的看一下这个字节码指令,这个流程并不像是一个循环的流程,而是一个方法顺序执行的流程:

先初始化一些数据

执行invokedynamic指令(暂时这个指令是做什么的)

然后执行java/util/List.forEach()方法,所以真正的循环逻辑在这里

所以我们可以发现,使用Lambda表达式循环时,在循环前会做一些其他事情,所以导致执行时间要更长一点。

那么invokedynamic指令到底做了什么事情呢?

java/util/List.forEach方法接收一个参数Consumer super T> action,Consumer是一个接口,所以如果要调用这个方法,就要传递该接口类型的对象。

而我们在代码里实际上是传递的一个Lambda表达式,那么我们这里可以假设:需要将Lambda表达式转换成对象,且该对象的类型需要根据该Lambda表达式所使用的地方在编译时期进行反推。

这里在解释一下反推:一个Lambda表达式是可以被多个方法使用的,而且这个方法所接收的参数类型,也就是函数式接口,是可以不一样的,只要函数式接口符合该Lambda表达式的定义即可。

本例中,编译器在编译时可以反推出,Lambda表达式对应一个Cosumer接口类型的对象。

那么如果要将Lambda表达式转换成一个对象,就需要有一个类实现Consumer接口。

所以,现在的问题就是这个类是什么时候生成的,并且生成在哪里了?

所以,我们慢慢的应该能够想到,invokedynamic指令,它是不是就是先将Lambda表达式转换成某个类,然后生成一个实例以便提供给forEach方法调用呢?

我们回头再看一下invokedynamic指令:

Java中调用函数有四大指令:invokevirtual、invokespecial、invokestatic、invokeinterface,在JSR 292 添加了一个新的指令invokedynamic,这个指令表示执行动态语言,也就是Lambda表达式。

该指令注释中的#0表示的是BootstrapMethods中的第0个方法:

所以invokedynamic执行时,实际上就是执行BootstrapMethods中的方法,比如本例中的:java/lang/invoke/LambdaMetafactory.metafactory。

代码如下:

这个方法中用到了一个特别明显且易懂的类:InnerClassLambdaMetafactory。

这个类是一个针对Lambda表达式生成内部类的工厂类。当调用buildCallSite方法是会生成一个内部类并且生成该类的一个实例。

那么现在要生成一个内部类,需要一些什么条件呢:

类名:可按一些规则生成

类需要实现的接口:编译时就已知了,本例中就是Consumer接口

实现接口里面的方法:本例中就是Consumer接口的void accept(T t)方法。

那么内部类该怎么实现void accept(T t)方法呢?

我们再来看一下javap -v -p Test.class的结果中除开我们自己实现的方法外还多了一个方法:

很明显,这个静态的lambda$main$0方法代表的就是我们写的Lambda表达式,只是因为我们例子中Lambda表达式没写什么逻辑,所以这段字节码指令Code部分也没有什么内容。

那么,我们现在在实现内部类中的void accept(T t)方法时,只要调用一个这个lambda$main$0静态方法即可。

所以到此,一个内部类就可以被正常的实现出来了,内部类有了之后,Lambda表达式就是可以被转换成这个内部类的对象,就可以进行循环了。

java lamdba表达式效率_java8新特性Lambda表达式为什么运行效率低相关推荐

  1. java lambda表达式详解_Java8新特性Lambda表达式详解

    课程目标: 通过本课程的学习,详细掌握Java8新特性之Lambda表达式: 适用人群:有Java基础的开发人员: 课程概述:从Java 8出现以来lambda是最重要的特性之一,它可以让我们用简洁流 ...

  2. java三目表达式_Java8新特性Lambda表达式

    1 Lambda表达式 对于很多计算机语言来说,Lambda表达式并不是陌生的语法格式,而对于Java而言,它的到来比较晚,直到Java8更新之后,Lambda表达式才正式出现在Java语法中.所以如 ...

  3. java lambda表达式详解_java8新特性-Lambda表达式的详解(从0开始)

    这几天复习了java8的一些新特性,作为一个从java5以来最具革命性的版本,一直没有来得及总结.本系列文章主要是从<java8实战>总结的.这是第一篇文章主要介绍java8的lambda ...

  4. 【Java】jdk 1.8 新特性——Lambda表达式

    Lambda表达式 jdk 1.8 新加入的特性,简化了简单接口的实现 函数式接口 函数式中只有一个待实现的方法,可以使用@FunctionalInterface注解标注函数式接口.这个接口中只能有一 ...

  5. java8 lambda判断_Java8新特性-Lambda表达式

    Predicate接口使用 请在测试类main方法中完成以下需求 已知有Integer[] arr = {-12345, 9999, 520, 0,-38,-7758520,941213} a) 使用 ...

  6. Java 8 新特性 lambda表达式

    / Created by Manager on 2021/4/1. Java 8 新特性 lambda表达式 StreamAPI 新日期 新注解 */ 视频连接 1https://www.bilibi ...

  7. Java 8 新特性Lambda 表达式

    Java 8 新特性Lambda 表达式 一.常用循环 二.匿名内部类 三.排序集合 四.循环打印对象 五.根据条件修改 六.排序 七.求和 八.统计方法 九.材料 一.常用循环 public cla ...

  8. java新特性lambda表达式快速入门

    文章目录 序 常规写法 写法一:新建类并实现该接口 写法二:使用匿名函数 lambda写法 写法一:lambda常规写法 写法二:lambda简写 中场疑问 lambda的方法引用 写法一 写法二 练 ...

  9. Java8新特性----Lambda表达式详细探讨

    Java8新特性 Lambda表达式 入门演示 案例1 如何解决 cannot be cast to java.lang.Comparable问题? 案例2 优化方式一 : 策略设计模式 优化方式二: ...

最新文章

  1. SQL语言 之 数据查询
  2. 百度api:根据经纬度获取地理位置信息
  3. 如何实际使用强类型Dataset(键盘再也不需要敲入表名和字段名了)
  4. bootstrap-multiselect样式修改
  5. 初始化 数组 java_如何用Java初始化数组
  6. BZOJ 3831 POI2014 Litter Bird
  7. python折线图实线虚线_python – matplotlib中的虚线而不是缺失值
  8. 四大银行(工建农中)大数据进展如何?
  9. 数据统计学习的5个基本流程
  10. FPGA实现SPI 协议
  11. NSIS 注册DLL OCX
  12. 最全面测试计划书模板
  13. TimeZone.getTimeZone 时区参数说明
  14. 利用OpenCV实现一个简单的实时人脸检测项目并显示FPS
  15. CCITT对媒体的分类
  16. SAP中的电子数据交换EDI
  17. 瓦伦达心态——再谈工作中的专注和心态
  18. 大数据架构选型与设计
  19. 鸿蒙实力等级划分,从综漫开始的万界之旅
  20. 招聘 iOS 全栈工程师 欢迎加入 ArcBlock 中国研发中心

热门文章

  1. “智慧血联网平台”亮相军民融合技术装备博览会
  2. 【转载】有软件开发,就要有软件测试!
  3. node.js学习笔记之promise
  4. 电脑中所有exe文件无法运行解决方案
  5. Cisco *** 完全配置指南-连载-PIX和ASA连接的故障诊断与排除
  6. 点点看   只有想不到没有看不到
  7. Python基础14-迭代器与生成器
  8. 关系数据库非关系数据库_如何与关系数据库最佳配合
  9. 10、同步机制遵循的原则_我要遵循的10条原则
  10. vs2017 open从v_宣布#Open2017,这是面向开发人员的除夕直播流