原文出处:ImportNew

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

Java 8 中的 Lambda 表达式和流(Stream)受到了热烈欢迎。这是 Java 迄今为止最令人激动的特征。这些新的语言特征允许采用函数式风格来进行编码,我们可以用这些特性完成许多有趣的功能。这些特性如此有趣以至于被认为是不合理的。我们对此表示怀疑,于是决定对这些特性进行测试。

我们创建一个简单的任务:从一个 ArrayList 找出最大值,将传统方式与 Java 8 中的新方式进行测试比较。说实话,测试的结果让我感到非常惊讶。

命令式风格与 Java 8 函数式编程风格比较

我喜欢直接进入主题,所以先看一下结果。为了做这次基准测试,我们先创建了一个 ArrayList,并插入一个 100000 个随机整数,并通过 7 种不同的方式遍历所有的值来查找最大值。实现分为两组:Java 8 中引入的函数式风格与 Java 一直使用的命令式风格。

这是每个方法耗费的时长:

最大错误记录是并行流上的 0.042,完整输出结果在这篇文章结尾部分可以看到。

小贴士:

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

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

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

Lambda 表达式的速度介于流与并行流之间。这个结果确实挺令人惊讶的,因为 lambda 表达式的实现方式是基于流的 API 来实现的。

不是所有的情况都如上所示:当我们想演示在 lambda 表达式和流中很容易犯错时,我们收到了很多社区的反馈,要求我们优化基准测试代码,如消除整数的自动装包和解包操作。第二次测试(已优化)的结果在这篇文章结束位置可以看到。

让我们快速看一下每个方法,按照运行速度由快到慢:

命令式风格

iteratorMaxInteger()——使用迭代器遍历列表:

1
2
3
4
5
6
7
public int iteratorMaxInteger() {
    int max = Integer.MIN_VALUE;
    for (Iterator it = integers.iterator(); it.hasNext(); ) {
        max = Integer.max(max, it.next());
    }
    return max;
}

forEachLoopMaxInteger()——不使用迭代器,使用 For-Each 循环遍历列表(不要误用 Java 8 的 forEach)

1
2
3
4
5
6
7
public int forEachLoopMaxInteger() {
    int max = Integer.MIN_VALUE;
    for (Integer n : integers) {
        max = Integer.max(max, n);
    }
    return max;
}

forMaxInteger()——使用简单的 for 循环和索引遍历列表:

1
2
3
4
5
6
7
public int forMaxInteger() {
    int max = Integer.MIN_VALUE;
    for (int i = 0; i < size; i++) {
        max = Integer.max(max, integers.get(i));
    }
    return max;
}

函数式风格

parallelStreamMaxInteger()——使用 Java 8 并行流遍历列表:

1
2
3
4
public int parallelStreamMaxInteger() {
    Optional max = integers.parallelStream().reduce(Integer::max);
    return max.get();
}

lambdaMaxInteger()——使用 lambda 表达式及流遍历列表。优雅的一行代码:

1
2
3
public int lambdaMaxInteger() {
    return integers.stream().reduce(Integer.MIN_VALUE, (a, b) -> Integer.max(a, b));
}

forEachLambdaMaxInteger()——这个用例有点混乱。可能是因为 Java 8 的 forEach 特性有一个很烦人的东西:只能使用 final 变量,所以我们创建一个 final 包装类来解决该问题,这样我们就能访问到更新后的最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int forEachLambdaMaxInteger() {
    final Wrapper wrapper = new Wrapper();
    wrapper.inner = Integer.MIN_VALUE;
 
    integers.forEach(i -> helper(i, wrapper));
    return wrapper.inner.intValue();
}
 
public static class Wrapper {
    public Integer inner;
}
 
private int helper(int i, Wrapper wrapper) {
    wrapper.inner = Math.max(i, wrapper.inner);
    return wrapper.inner;
}

顺便提一下,如果要讨论 forEach,我们提供了一些有趣的关于它的缺点的见解,答案参见StackOverflow

streamMaxInteger()——使用 Java 8 的流遍历列表:

1
2
3
4
public int streamMaxInteger() {
    Optional max = integers.stream().reduce(Integer::max);
    return max.get();
}

优化后的基准测试

根据这篇文章的反馈,我们创建另一个版本的基准测试。源代码的不同之处可以在这里查看。下面是测试结果:

最后的思考

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

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

Java8 Lambda表达式和流操作如何让你的代码变慢5倍的更多相关文章

  1. Java8 Lambda表达式详解手册及实例

    先贩卖一下焦虑,Java8发于2014年3月18日,距离现在已经快6年了,如果你对Java8的新特性还没有应用,甚至还一无所知,那你真得关注公众号"程序新视界",好好系列的学习一下 ...

  2. 【Java学习笔记之三十一】详解Java8 lambda表达式

    Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...

  3. java8 快速入门 lambda表达式 Java8 lambda表达式10个示例

    本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...

  4. Java8 lambda表达式10个示例

    Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Ja ...

  5. java8 Lambda表达式的10个例子(转)

    原文:http://jobar.iteye.com/blog/2023477 Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 Java代码 收藏代码// ...

  6. Java8 lambda表达式10个示例<转>

    例1.用lambda表达式实现Runnable 我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例.看一下Java 8之前的runna ...

  7. java8 lambda表达式应用

    1.用lambda表达式实现Runnable非常简单// Java 8之前: new Thread(new Runnable() { @Override public void run() { Sys ...

  8. java8 Lambda表达式的新手上车指南(1)

    背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...

  9. java8 Lambda表达式的新手上车指南(1)--基础语法和函数式接口

    背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...

随机推荐

  1. umeng 渠道统计ios

    1. 使用前提:需要在工程中添加依赖框架:AdSupport.framework 和 Security.framework 2.在AppDelegate.m文件中的-(BOOL)application ...

  2. iOS 7 与 Xamarin - MultiPeer Connectivity(转载)

    随着时代的改变,移动设备在生活工作都开始取代原有的pc.设备间的数据交互就成为了必备可少的功能.比较成熟的产品有NFC.这个啪啪的操作很流行,例如分享图片,分享文件等 .但是在iOS设备中还没有NFC ...

  3. Android touch事件处理流程

    前面我们看了key事件的处理流程,相信大家对此已经有了新的认识,这篇文章我打算带领大家来看看稍微复杂些的touch 事件的处理流程.说它复杂是因为key事件本身就key down,up,long pr ...

  4. 网络编程---HTTP

    URL: 什么是URL ? URL的全称是Uniform Resource Locator(统一资源定位符) 通过1个URL,能找到互联网上唯一的1个资源 URL就是资源的地址.位置,互联网上的每个资 ...

  5. 数据存储与IO(二)

    一.NSBundle资源包. 只要把文件拖到Xcode左边项目导航面板中,选择复制文件到项目中,该文件就包含进bundle中了.用[NSBundle mainBundle]获取应用程序包,常用的方法: ...

  6. WPF学习之路(十一)布局(续)

    布局实际上是一个Slot模型,其中每个父对象分配给子对象一个Slot,子对象可以自由占用Slot中的空间,通过Margin\VerticalAlignment\HorizontalAlignment控 ...

  7. HTML基础(一)——一般标签、常用标签和表格

    第一部分  HTML <html>    --开始标签 <head> 网页上的控制信息 <title>页面标题</title> </head> ...

  8. 创建docker镜像,初始化jdk8与tomcat环境

    一.创建Dockerfile文件: 创建Dockerfile文件,下载jdk与tomcat放在Dockerfile同目录下. Dockerfile文件内容: FROM Ubuntu:14.10 MAI ...

  9. 关于iOS构建版本提交iTunes后,一直不出现,没加号的解决方案

    最近第一次遇到,正常打包,上传iTunes App Store,都能正常upload. 也可能是因为刚升了Xcode 8 的缘故,莫名其妙的小问题... 描述如下: 如果进iTunes的活动界面,也能 ...

  10. DataTable去除重复行,根据某一字段进行distinct

    网上有很多方法,比如利用视图处理: //去掉重复行 DataView dv = table.DefaultView; table = dv.ToTable(true, new string[] { & ...