文
章
目
录
章
目
录
Java提供了许多迭代列表的方法。其中一些方法包括:
我们不会详细讨论上述每种方法的基本知识,因为这超出了本文的范围,而且大多数人都已经很了解。
在本文中,我们将比较所有循环方法针对相同的数据集的相对性能。
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循环。