实现Java集合迭代的高性能

网友投稿 618 2022-05-30

实现Java集合迭代的高性能

一、介绍

Java开发者经常会遇到处理集合比如ArrayList、HashSet的情况Java 8也提供了Lambda表达式和Streaming API来简化集合相关的工作。在大多数应用场景下无需考虑集合迭代的性能消耗。但是在一些极端情况下比如集合包含了上百万条记录的情况这个时候集合迭代就需要选择正确的姿势否则性能会较差。

实现Java集合迭代的高性能

使用JMH检查下面每段代码片段的运行时间。

二、forEach vs. C Style vs. Stream API

迭代是一个非常基本的功能所有的编程语言都有简单的迭代语法允许程序员在集合上运行迭代。Stream API可以通过Collections用非常直接的方式进行迭代。

public List streamSingleThread(BenchMarkState state) {     List result = new ArrayList<>(state.testData.size());     state.testData.stream().forEach(item -> {         result.add(item);     });     return result; } public List streamMultiThread(BenchMarkState state) {     List result = new ArrayList<>(state.testData.size());     state.testData.stream().parallel().forEach(item -> {         result.add(item);     });     return result; }

使用forEach循环也非常简单

public List forEach(BenchMarkState state) {     List result = new ArrayList<>(state.testData.size());     for(Integer item : state.testData) {         result.add(item);     }     return result; }

C style方式的迭代其代码要冗长一些但仍然非常紧凑

public List forCStyle(BenchMarkState state) {     int size = state.testData.size();     List 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 style方式的迭代JVM只是简单地增加了一个整型变量它直接从内存读值。这使它非常快。但forEach迭代则不同根据Oracle官方文档JVM必须把forEach转换为迭代器并为每个数据项调用hasNext()。这就是为什么forEach比C style迭代慢。

forEach文档(https://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html)

三、哪一种迭代的性能最高

我们定义测试数据

@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 testData = new HashSet<>(500000); }

Java Set同时支持Stream API和forEach循环。根据前面的测试如果我们把Set转换为ArrayList看看性能是否有所提升。

public List forCStyle(BenchMarkState state) {     int size = state.testData.size();     List 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 style组合迭代的循环

public List forCStyleWithIteration(BenchMarkState state) {  int size = state.testData.size();  List result = new ArrayList<>(size);  Iterator iteration = state.testData.iterator();  for(int j = 0; j < size; j ++) {  result.add(iteration.next());  }  return result;  }

forEach

public List forEach(BenchMarkState state) {     List 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 (HashSet uses HashMap

结论

在集合Collections上使用Foreach和Stream API是非常便利的方法写这样的代码也显得精炼。但要记住当系统需要考虑性能和稳定性因素时就应该改写这些循环。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ServiceComb中的“卖身协议”
下一篇:基于WeLink开发的智慧云OA系统获奖啦!
相关文章