JavaRush /Java Blog /Random-TW /喝咖啡休息#137。For 迴圈或 Foreach - Java 中哪一個比較快?循環遍歷 Java 映射中每個條...

喝咖啡休息#137。For 迴圈或 Foreach - Java 中哪一個比較快?循環遍歷 Java 映射中每個條目的 8 種有效方法

在 Random-TW 群組發布

For 迴圈或 Foreach - Java 中哪一個比較快?

來源:Medium 幾年前,當我找工作時,我在面試中被問到的問題之一是我們是否應該使用forforEach來迭代ArrayList關於forEachfor之間偏好差異的爭論已經存在很長時間了。我的印像是forEach比較快。但最後我意識到我錯了。僅供參考, Java 1.5 中引入的forEach循環(或改進的for循環)透過完全隱藏迭代器或索引變數來消除混亂和錯誤的可能性。我相信forforEach之間唯一的實際區別是,對於索引對象,我們無法存取索引。 喝咖啡休息#137。 For 迴圈或 Foreach - Java 中哪一個比較快? 循環遍歷 Java 映射中每個條目的 8 種有效方法 - 1
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 */
從上面的字節碼我們看到:
  1. getfield指令用於取得整數變數。

  2. 呼叫List.iterator取得迭代器實例。

  3. 呼叫iterator.hasNext。如果傳回 true,則應該呼叫iterator.next方法。

讓我們執行一個效能測試。在主IterateListTest方法中,我建立了一個清單並使用forforEach循環對其進行迭代。
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喝咖啡休息#137。 For 迴圈或 Foreach - Java 中哪一個比較快? 迭代 Java 映射中每個條目的 8 種有效方法 - 2迴圈的效能優於forEach迴圈。如果您使用LinkedList而不是ArrayList,您可以看到forEach 的效能比LinkedList更好。 ArrayList內部使用陣列來儲存元素。由於陣列是連續的記憶體區域,因此時間複雜度為 O(1)。這是因為資料是透過索引檢索的。 LinkedList使用雙向鍊錶。當我們使用for迴圈實作遍歷時,每次都是從鍊錶的頭節點開始,所以時間複雜度為O(n*n)。

循環遍歷 Java 映射中每個條目的 8 種有效方法

資料來源:Medium 上週,一位實習生問我如何迭代 Java Map。我回答說,因為很簡單,這個問題的答案總是在Google上。一段時間後,她把StackOverflow上的頁面地址寄給我,結果發現有很多人在關注這個問題。因此,我決定深入探討迭代問題,並與大家分享幾種實現迭代的方法。 喝咖啡休息#137。 For 迴圈或 Foreach - Java 中哪一個比較快? 迭代 Java 映射中每個條目的 8 種有效方法 - 3

1.使用迭代器和Map.Entry

@Test
public void test1_UsingWhileAndMapEntry(){
    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
    System.out.println(i);
}

2.使用foreach和Map.Entry

@Test
public void test2_UsingForEachAndMapEntry(){
    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
    System.out.println(i);
}

3. 從 Java 8 使用 foreach

@Test
public void test3_UsingForEachAndJava8(){
    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    System.out.println(i[0]);
}

4.使用keySet和foreach

@Test
public void test4_UsingKeySetAndForEach(){
    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    System.out.println(i);
}

5.使用keySet和iterator

@Test
public void test5_UsingKeySetAndIterator(){
    long i = 0;
    Iterator<Integer> it = map.keySet().iterator();
    while (it.hasNext()) {
        Integer key = it.next();
        i += key + map.get(key);
    }
    System.out.println(i);
}

6. 使用 for 和 Map.Entry

@Test
public void test6_UsingForAndIterator(){
    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
    System.out.println(i);
}

7.使用Java 8 Stream API

@Test
public void test7_UsingJava8StreamApi(){
    System. out .println(map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}

8. Java 8 Stream API的平行使用

@Test
public void test8_UsingJava8StreamApiParallel(){
    System. out .println(map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}

每種方法在速度上的比較:

public final static Integer SIZE = 1000000;
public Map<Integer, Integer> map = toMap();
public Map<Integer, Integer> toMap(){
    map = new HashMap<>(SIZE);
    for (int i = 0; i < SIZE; i++) {
        map.put(i, i);
    }
    return map;
}
我們得到: 喝咖啡休息#137。 For 迴圈或 Foreach - Java 中哪一個比較快? 循環遍歷 Java 映射中每個條目的 8 種有效方法 - 4喝咖啡休息#137。 For 迴圈或 Foreach - Java 中哪一個比較快? 迭代 Java 映射中每個條目的 8 個有效方法 - 5喝咖啡休息#137。 For 迴圈或 Foreach - Java 中哪一個比較快? 迭代 Java 映射中每個條目的 8 種有效方法 - 6

結論

透過比較數據可以看出,當數量較少時,方法6耗時最長,方法8耗時最長,而當數量較多時,由於方法8是同時執行的,所以耗時最少。有趣的是,測試執行順序總是 for -> while -> foreach/stream 我不知道為什麼:(
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION