摘要:本文由葡萄城技術團隊於博客園發佈。轉載請註明出處:葡萄城官網,葡萄城爲開發者提供專業的開發工具、解決方案和服務,賦能開發者。
前言
在Java編程中,循環結構是程序員常用的控制流程,而for循環和foreach循環是其中比較常見的兩種形式。關於它們哪一個更快的討論一直存在。本文旨在探究Java中的for循環和foreach循環的性能差異,並幫助讀者更好地選擇適合自身需求的循環方式。通過詳細比較它們的遍歷效率、數據結構適用性和編譯器優化等因素,我們將爲大家揭示它們的差異和適用場景,以便您能夠做出更明智的編程決策。
for循環與foreach循環的比較
小編認爲for和foreach 之間唯一的實際區別是,對於可索引對象,我們無權訪問索引。
for(int i = 0; i < mylist.length; i++) {
if(i < 5) {
//do something
} else {
//do other stuff
}
}
但是,我們可以使用 foreach 創建一個單獨的索引 int 變量。例如:
int index = -1;
for(int myint : mylist) {
index++;
if(index < 5) {
//do something
} else {
//do other stuff
}
}
現在寫一個簡單的類,其中有 foreachTest() 方法,該方法使用 forEach 迭代列表。
import java.util.List;
public class ForEachTest {
List<Integer> intList;
public void foreachTest(){
for(Integer i : intList){
}
}
}
編譯這個類時,編譯器會在內部將這段代碼轉換爲迭代器實現。小編通過執行 javap -verbose IterateListTest 反編譯代碼。
public void foreachTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=1
0: aload_0
1: getfield #19 // Field intList:Ljava/util/List;
4: invokeinterface #21, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
9: astore_2
10: goto 23
13: aload_2
14: invokeinterface #27, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
19: checkcast #33 // class java/lang/Integer
22: astore_1
23: aload_2
24: invokeinterface #35, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
29: ifne 13
32: return
LineNumberTable:
line 9: 0
line 12: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 this Lcom/greekykhs/springboot/ForEachTest;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 13
locals = [ class com/greekykhs/springboot/ForEachTest, top, class java/util/Iterator ]
stack = []
frame_type = 9 /* same */
從上面的字節碼我們可以看到:
a). getfield命令用於獲取變量整數。
b).調用List.iterator獲取迭代器實例
c).調用iterator.hasNext,如果返回true,則調用iterator.next方法。
下邊來做一下性能測試。在 IterateListTest 的主要方法中,創建了一個列表並使用 for 和 forEach 循環對其進行迭代。
import java.util.ArrayList;
import java.util.List;
public class IterateListTest {
public static void main(String[] args) {
List<Integer> mylist = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
mylist.add(i);
}
long forLoopStartTime = System.currentTimeMillis();
for (int i = 0; i < mylist.size(); i++) {mylist.get(i);}
long forLoopTraversalCost =System.currentTimeMillis()-forLoopStartTime;
System.out.println("for loop traversal cost for ArrayList= "+ forLoopTraversalCost);
long forEachStartTime = System.currentTimeMillis();
for (Integer integer : mylist) {}
long forEachTraversalCost =System.currentTimeMillis()-forEachStartTime;
System.out.println("foreach traversal cost for ArrayList= "+ forEachTraversalCost);
}
}
結果如下:
總結
觀察結果顯示,for循環的性能優於for-each循環。然後再使用LinkedList比較它們的性能差異。對於 LinkedList 來說,for-each循環展現出更好的性能。ArrayList內部使用連續存儲的數組,因此數據的檢索時間複雜度爲 O(1),通過索引可以直接訪問數據。而 LinkedList 使用雙向鏈表結構,當我們使用 for 循環進行遍歷時,每次都需要從鏈表頭節點開始,導致時間複雜度達到了 O(n*n),因此在這種情況下,for-each 循環更適合操作 LinkedList。