Java集合循环性能比较

介绍

Java开发人员通常处理ArrayList和HashSet等集合。Java 8附带了lambda和streaming API,帮助我们轻松处理集合。在大多数情况下,我们只处理几千个条目的集合,而性能并不重要。但是,在某些极端的情况下,当我们不得不多次超过数百万件条目的集合时,性能就会变得很糟糕。

我使用JMH检查每个代码段的运行时间。

forEach vs. C语言风格循环 vs. Stream API

迭代是一个基本特性。所有编程语言都有简单的语法,允许程序员在集合中进行迭代。而 streaming API可以以非常简单的方式对集合进行迭代。

public List<Integer> streamSingleThread(BenchMarkState state){

List<Integer> result = new ArrayList<>(state.testData.size());

state.testData.stream().forEach(item -> {

result.add(item);

});

return result;

}

public List<Integer> streamMultiThread(BenchMarkState state){

List<Integer> result = new ArrayList<>(state.testData.size());

state.testData.stream().parallel().forEach(item -> {

result.add(item);

});

return result;

}

forEach 迭代代码很简单:

public List<Integer> forEach(BenchMarkState state){

List<Integer> result = new ArrayList<>(state.testData.size());

for(Integer item : state.testData){

result.add(item);

}

return result;

}

C语言风格代码比较冗长,但仍然非常紧凑:

public List<Integer> forCStyle(BenchMarkState state){

int size = state.testData.size();

List<Integer> result = new ArrayList<>(size);

for(int j = 0; j < size; j ++){

result.add(state.testData.get(j));

}

return result;

}

然后,查看性能比较:

Benchmark Mode Cnt Score Error Units

TestLoopPerformance.forCStyle avgt 200 18.068 ± 0.074 ms/op

TestLoopPerformance.forEach avgt 200 30.566 ± 0.165 ms/op

TestLoopPerformance.streamMultiThread avgt 200 79.433 ± 0.747 ms/op

TestLoopPerformance.streamSingleThread avgt 200 37.779 ± 0.485 ms/op

使用C风格的循环代码,JVM只增加一个整数,然后直接从内存中读取值。这使它运行效率非常快。

但是forEach是非常不同的,根据从StackOverFlow和Oracle文档上获得的答案,JVM必须将forEach转换为迭代器,并对每个条目调用hasNext()。

这就是为什么forEach比C语言风格代码慢。

哪种是高性能的集合遍历方式?

我们定义测试数据:

@State(Scope.Benchmark)

public static class BenchMarkState {

@Setup(Level.Trial)

public void doSetup() {

for(int i = 0; i < 500000; i++){

testData.add(Integer.valueOf(i));

}

}

@TearDown(Level.Trial)

public void doTearDown() {

testData = new HashSet<>(500000);

}

public Set<Integer> testData = new HashSet<>(500000);

}

Java集还支持流API和forEach循环。根据前面的测试,如果我们将Set转换为ArrayList,然后遍历ArrayList,性能可能会提高吗?

public List<Integer> forCStyle(BenchMarkState state){

int size = state.testData.size();

List<Integer> result = new ArrayList<>(size);

Integer[] temp = (Integer[]) state.testData.toArray(new Integer[size]);

for(int j = 0; j < size; j ++){

result.add(temp[j]);

}

return result;

}

将迭代器与C风格的循环的组合在一起如何呢?

public List<Integer> forCStyleWithIteration(BenchMarkState state){

int size = state.testData.size();

List<Integer> result = new ArrayList<>(size);

Iterator<Integer> iteration = state.testData.iterator();

for(int j = 0; j < size; j ++){

result.add(iteration.next());

}

return result;

}

或者,只是简单的循环?

public List<Integer> forEach(BenchMarkState state){

List<Integer> result = new ArrayList<>(state.testData.size());

for(Integer item : state.testData) {

result.add(item);

}

return result;

}

这是一个很好的想法,但是它不起作用,因为初始化新的ArrayList也会消耗资源。

Benchmark Mode Cnt Score Error Units

TestLoopPerformance.forCStyle avgt 200 6.013 ± 0.108 ms/op

TestLoopPerformance.forCStyleWithIteration avgt 200 4.281 ± 0.049 ms/op

TestLoopPerformance.forEach avgt 200 4.498 ± 0.026 ms/op

HashMap (HashMap使用HashMap)不是为迭代所有项而设计的。遍历HashMap的最快方法是将Iterator和C样式的循环结合起来,因为JVM不必调用hasNext()。

结论

Foreach和Stream API可以方便地处理集合。您可以更快地编写代码。但是,当您的系统对稳定和性能要求很高时,您应该考虑编写合适的循环代码。

原文发布于微信公众号 - 程序你好(codinghello)

原文发表时间:2018-07-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发技术

排序之堆排序

  本篇博客是在伍迷兄的博客基础上进行的,其博客地址点击就可以进去,里面好博客很多,我的排序算法都来自于此;一些数据结构方面的概念我就不多阐述了,伍迷兄的博客中...

10620
来自专栏浪淘沙

Java学习day16---人际关系

13950
来自专栏林欣哲

HashMap解析

12830
来自专栏微信公众号:Java团长

高效遍历Java容器

通过本文,你可以更深入的学习 Java 语言中 forEach 语法的知识,以及它和 C 语言形式的 for 循环、 Steam API 的对比。

25640
来自专栏zaking's

用js来实现那些数据结构13(树01-二叉搜索树的实现)

  前一篇文章我们学会了第一个非顺序数据结构hashMap,那么这一篇我们来学学树,包括树的概念和一些相关的术语以及二叉搜索树的实现。唉?为什么不是树的实现,不...

475100
来自专栏Janti

HashMap 学习心得

1.构造 HashMap 底层数据结构线性数组,HashMap有一个静态内部类Entry,Entry有四个属性,key,value,next,hash ? En...

38460
来自专栏java一日一条

Java中HashMap详解

HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,...

16730
来自专栏非著名程序员

2016腾讯软件开发面试题之不定项选择题

一、前言 2017年1月27日19:05:28,今天是年三十,首先祝大家新年快乐,之前对自己要求过,每星期一篇面试题的博客,虽然今天心里有一万个不愿意写,也还是...

389100
来自专栏小鄧子的技术博客专栏

算法与数据结构(2),Map

睡了不到六个小时,被一个很奇葩又很奇怪的梦吓醒,以最快的速度穿好衣服,跑下楼去买了杯咖啡上来,文字没写多少,咖啡倒是一饮而尽。

9910
来自专栏小狼的世界

Leetcode刷题记录:构建最大数二叉树

给定一个不含重复数字的数组,最大二叉树构建规则如下: 1、根是数组中最大的数字 2、左边的子树是最大数字左边的内容 3、右边的子树是最大数字右边的内容

12120

扫码关注云+社区

领取腾讯云代金券