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循环。