前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java集合循环性能比较

Java集合循环性能比较

作者头像
程序你好
发布2018-07-23 10:15:51
8460
发布2018-07-23 10:15:51
举报
文章被收录于专栏:程序你好程序你好

介绍

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可以方便地处理集合。您可以更快地编写代码。但是,当您的系统对稳定和性能要求很高时,您应该考虑编写合适的循环代码。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序你好 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • forEach vs. C语言风格循环 vs. Stream API
  • 哪种是高性能的集合遍历方式?
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档