共计 2006 个字符,预计需要花费 6 分钟才能阅读完成。
导读 | 对于 Java 中的 For 循环和 Foreach,哪个更快?通过本文,您可以了解一些集合遍历技巧。 |
Java 遍历集合有两种方法。一个是最基本的 for 循环,另一个是 jdk5 引入的 for each。通过这种方法,我们可以更方便地遍历数组和集合。但是你有没有想过这两种方法?哪一个遍历集合更有效?
For-each 不是一种新语法,而是 Java 的语法糖。在编译时,编译器将此代码转换为迭代器实现,并将其编译为字节码。
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。
我们可以通过执行命令 javap-verbose-Testforeach 反编译以下编译代码:
public class TestForeach {List 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 arrayList = new ArrayList();
for (int i = 0; i
这是测试结果:
如你所见,结果是显而易见的。对于 ArrayList,使用 For 循环方法的性能优于 For each 方法。
我们可以说 for 循环比 for-each 好吗?
答案是否定的。在下一个基准测试中,我们将 ArrayList 更改为 LinkedList。同样,这里是测试结果。
一些初学者可能想知道为什么 ArrayList 使用 for 循环方法遍历得更快,而 LinkedList 则更慢,速度也非常慢?
这由 ArrayList 和 LinkedList 数据结构决定。ArrayList 底层使用数组存储元素。数组是连续的内存空间。数据可以通过索引获得。时间复杂度为 O(1),因此速度很快。
LinkedList 的底层是一个双向链表。使用 for 循环实现遍历,每次都需要从链表的头节点开始。时间复杂度为 O(n*n)。
1. 使用 ArrayList 时,for 循环方法更快,因为 for-each 由迭代器实现,并且需要执行并发修改验证。
2. 使用 LinkedList 时,for-each 比 for 循环快得多,因为 LinkedList 是通过使用双向链表实现的。每个寻址都需要从头节点开始。如果我们需要遍历 LinkedList,我们需要避免使用 for 循环。
3. 使用迭代器模式,for-each 不需要关心集合的具体实现。如果需要替换集合,无需修改代码即可轻松替换。