本文共 1944 字,大约阅读时间需要 6 分钟。
Java开发者经常会遇到处理集合(比如ArrayList、HashSet)的情况,Java 8也提供了Lambda表达式和Streaming API来简化集合相关的工作。在大多数应用场景下,无需考虑集合迭代的性能消耗。但是,在一些极端情况下,比如集合包含了上百万条记录的情况,这个时候集合迭代就需要选择正确的姿势,否则性能会较差。
使用JMH检查下面每段代码片段的运行时间。
迭代是一个非常基本的功能,所有的编程语言都有简单的迭代语法,允许程序员在集合上运行迭代。Stream API可以通过Collections用非常直接的方式进行迭代。
public ListstreamSingleThread(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 ListforEach(BenchMarkState state) { List result = new ArrayList<>(state.testData.size()); for(Integer item : state.testData) { result.add(item); } return result;}
C style方式的迭代其代码要冗长一些,但仍然非常紧凑:
public ListforCStyle(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