Java中多种循环性能的区别比较
Java提供了许多迭代列表的方法。其中一些方法包括:
- Stream API
- ListIterator接口
- 增强型for循环
- 简单for循环
我们不会详细讨论上述每种方法的基本知识,因为这超出了本文的范围,而且大多数人都已经很了解。
在本文中,我们将比较所有循环方法针对相同的数据集的相对性能。
1.遍历列表的不同方法
我们列出了我所知的4种不同的方法。
1.1. Stream API
Java 8 Stream API提供了一种迭代集合并对每个元素进行操作的方式。流可以用作for循环的替代方法。
private static List<Integer> list = new ArrayList<>(); list.stream().forEach(consumerAction);
1.2. 增强型for循环
在这种技术中,使用Java 5中引入的高级for-each语句。
private static List<Integer> list = new ArrayList<>(); for(Integer i : list) { // do other stuff }
1.3. ListIterator接口
private static List<Integer> list = new ArrayList<>(); list.listIterator().forEachRemaining(consumerAction);
1.4. 简单for循环
private static List<Integer> list = new ArrayList<>(); int size = list.size(); for(int j = 0; j < size ; j++) { //do stuff }
2.性能比较
我们创建了一个ArrayList,并将其填充了一百万个整数实例。然后,我们将使用所有上述方法来遍历列表。这样我们就能够了解性能差异。
2.1. 执行环境
- Java 16
- Eclipse 2021-06
2.2. 源代码
import java.util.ArrayList; import java.util.List; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.infra.Blackhole; public class ForLoopPerformanceTest { public static void main(String[] args) throws Exception { org.openjdk.jmh.Main.main(args); } private static List<Integer> list = new ArrayList<>(); static { for(int i=0; i < 1_000_000; i++) { list.add(i); } } @Benchmark @Fork(value = 1, warmups = 1) @BenchmarkMode(Mode.Throughput) public void usingStream(Blackhole blackhole) { list.stream().forEach(i -> blackhole.consume(i)); } @Benchmark @Fork(value = 1, warmups = 1) @BenchmarkMode(Mode.Throughput) public void usingIterator(Blackhole blackhole) { list.listIterator().forEachRemaining(i -> blackhole.consume(i)); } @Benchmark @Fork(value = 1, warmups = 1) @BenchmarkMode(Mode.Throughput) public void usingForEachLoop(Blackhole blackhole) { for(Integer i : list) { blackhole.consume(i); } } @Benchmark @Fork(value = 1, warmups = 1) @BenchmarkMode(Mode.Throughput) public void usingSimpleForLoop(Blackhole blackhole) { for(int i = 0; i < list.size() ; i++) { blackhole.consume(list.get(i)); } } }
当基于JMH的基准运行时,控制台中的输出如下:
Benchmark Mode Cnt Score Error Units ForLoopPerformanceTest.usingForEachLoop thrpt 20 259.008 ± 17.888 ops/s ForLoopPerformanceTest.usingIterator thrpt 20 256.016 ± 10.342 ops/s ForLoopPerformanceTest.usingSimpleForLoop thrpt 20 495.308 ± 12.866 ops/s ForLoopPerformanceTest.usingStream thrpt 20 257.174 ± 15.880 ops/s
显然,使用简单的for循环在性能上要远远领先。其余三种方法提供了类似的性能数字。
3.结论
尽管简单的for循环提供了最佳性能,但其他循环方法提供了更好的可读性。
此外,我们正在使用包含超过百万个项目的列表,这在大多数应用程序中并不是一个非常实际的情况。
因此,如果列表中没有数百万个项目,可以使用新的Java功能,例如Stream API或增强型for循环。