对于Java循环中的For和For-each,哪个更快
Which is Faster For Loop or For-each in Java
对于Java循环中的For和For-each,哪个更快
通过本文,您可以了解一些集合遍历技巧。

Java遍历集合有两种方法。一个是最基本的for循环,另一个是jdk5引入的for each。通过这种方法,我们可以更方便地遍历数组和集合。但是你有没有想过这两种方法?哪一个遍历集合更有效?
for-each实现方法
For-each不是一种新语法,而是Java的语法糖([语法糖百度百科]( https://baike.baidu.com/item/语法糖/5247005#:~:text=语法糖(Syntactic sugar),也译为糖衣语法,是由英国 计算机 ,%E7%A7%91%E5%AD%A6%E5%AE%B6%E5%BD%BC%E5%BE%97%C2%B7%E7%BA%A6%E7%BF%B0%C2%B7%E5%85%B0%E8%BE%BE%EF%BC%88Peter%20J.%20Landin%EF%BC%89%E5%8F%91%E6%98%8E%E7%9A%84%E4%B8%80%E4%B8%AA%E6%9C%AF%E8%AF%AD%EF%BC%8C%E6%8C%87%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%AF%AD%E8%A8%80%E4%B8%AD%E6%B7%BB%E5%8A%A0%E7%9A%84%E6%9F%90%E7%A7%8D%E8%AF%AD%E6%B3%95%EF%BC%8C%E8%BF%99%E7%A7%8D%E8%AF%AD%E6%B3%95%E5%AF%B9%E8%AF%AD%E8%A8%80%E7%9A%84%E5%8A%9F%E8%83%BD%E5%B9%B6%E6%B2%A1%E6%9C%89%E5%BD%B1%E5%93%8D%EF%BC%8C%E4%BD%86%E6%98%AF%E6%9B%B4%E6%96%B9%E4%BE%BF%E7%A8%8B%E5%BA%8F%E5%91%98%E4%BD%BF%E7%94%A8%E3%80%82.%20%E9%80%9A%E5%B8%B8%E6%9D%A5%E8%AF%B4%E4%BD%BF%E7%94%A8%E8%AF%AD%E6%B3%95%E7%B3%96%E8%83%BD%E5%A4%9F%E5%A2%9E%E5%8A%A0%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%8F%AF%E8%AF%BB%E6%80%A7%EF%BC%8C%E4%BB%8E%E8%80%8C%E5%87%8F%E5%B0%91%E7%A8%8B%E5%BA%8F%E4%BB%A3%E7%A0%81%E5%87%BA%E9%94%99%E7%9A%84%E6%9C%BA%E4%BC%9A%E3%80%82.))。在编译时,编译器将此代码转换为迭代器实现,并将其编译为字节码。我们可以通过执行命令javap-verbose-Testforeach反编译以下编译代码:
public class TestForeach {
List<Integer> integers;
public void testForeach(){
for(Integer i : integers){
}
}
}
获得的详细字节码如下:
public void testForeach();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=1
0: aload_0
1: getfield #2 // Field integers:Ljava/util/List;
4: invokeinterface #3, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
9: astore_1
10: aload_1
11: invokeinterface #4, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
16: ifeq 32
19: aload_1
20: invokeinterface #5, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
25: checkcast #6 // class java/lang/Integer
28: astore_2
29: goto 10
32: return
LineNumberTable:
line 11: 0
line 13: 29
line 14: 32
LocalVariableTable:
Start Length Slot Name Signature
29 0 2 i Ljava/lang/Integer;
0 33 0 this Ltest/TestForeach;
}
此字节码的一般含义是使用getfileld命令来获取integers变量并且调用List.iterator来获取迭代器实例和调用iterator.hasNext。如果返回true,调用iterator.next方法。
请看,这是迭代器遍历集合的实现逻辑。
基准测试
现在让我们使用for循环方法和for-each方法进行测试。
public class ForLoopTest {
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
arrayList.add(i);
}
long arrayListStartTime = System.currentTimeMillis();
for (int i = 0; i < arrayList.size(); i++) {
arrayList.get(i);
}
long arrayListCost =System.currentTimeMillis()-arrayListStartTime;
System.out.println("ArrayList for loop traversal cost: "+ arrayListCost);
long arrayListForeachStartTime = System.currentTimeMillis();
for (Integer integer : arrayList) {
}
long arrayListForeachCost =System.currentTimeMillis()-arrayListForeachStartTime;
System.out.println("ArrayList foreach traversal cost: "+ arrayListForeachCost);
这是测试结果:

如你所见,结果是显而易见的。对于ArrayList,使用For循环方法的性能优于For each方法。
我们可以说for循环比for-each好吗?
答案是否定的。在下一个基准测试中,我们将ArrayList更改为LinkedList。
同样,这里是测试结果。

原因分析
一些初学者可能想知道为什么ArrayList使用for循环方法遍历得更快,而LinkedList则更慢,速度也非常慢?
这由ArrayList和LinkedList数据结构决定。
ArrayList底层使用数组存储元素。数组是连续的内存空间。数据可以通过索引获得。时间复杂度为O(1),因此速度很快。
LinkedList的底层是一个双向链表。使用for循环实现遍历,每次都需要从链表的头节点开始。时间复杂度为O(n*n)。
结论
- 使用ArrayList时,for循环方法更快,因为for-each由迭代器实现,并且需要执行并发修改验证。
- 使用LinkedList时,for-each比for循环快得多,因为LinkedList是通过使用双向链表实现的。每个寻址都需要从头节点开始。如果我们需要遍历LinkedList,我们需要避免使用for循环。
- 使用迭代器模式,for-each不需要关心集合的具体实现。如果需要替换集合,无需修改代码即可轻松替换。
欢迎关注公众号:愚生浅末。


浙公网安备 33010602011771号