มีความแตกต่างของประสิทธิภาพระหว่าง for for loop และ for for each หรือไม่?


184

ประสิทธิภาพการทำงานจะแตกต่างกันอย่างไรระหว่างสองลูปต่อไปนี้

for (Object o: objectArrayList) {
    o.DoSomething();
}

และ

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}

@Keparo: มันเป็น "for each" loop ไม่ใช่ "for-in" loop
Ande Turner

ในจาวาเรียกว่า "สำหรับแต่ละ" แต่เมื่อมันมาถึงวัตถุประสงค์ C มันเรียกว่า "สำหรับใน" ห่วง
damithH

โปรดดูเพิ่มเติมที่: docs.oracle.com/javase/8/docs/api/java/util/RandomAccess.html
Puce

การขยายสำหรับประสิทธิภาพของลูปถูกกล่าวถึงที่นี่: stackoverflow.com/questions/12155987/…
eckes

แม้ว่าจะมีความแตกต่างกัน แต่ก็เป็นเรื่องเล็กน้อยที่คุณควรให้ความสำคัญกับการอ่านเว้นแต่ว่าโค้ดนี้จะถูกประมวลผลเป็นล้านล้านครั้งต่อวินาที แล้วคุณจะต้องมีเกณฑ์มาตรฐานที่เหมาะสมเพื่อหลีกเลี่ยงการเพิ่มประสิทธิภาพก่อนวัยอันควร
Zabuzard

คำตอบ:


212

จากรายการ 46 ในจาวาที่มีประสิทธิภาพโดย Joshua Bloch:

สำหรับแต่ละลูปที่ถูกนำมาใช้ในรีลีส 1.5 กำจัดความยุ่งเหยิงและโอกาสในการเกิดข้อผิดพลาดโดยการซ่อนตัววนซ้ำหรือตัวแปรดัชนีอย่างสมบูรณ์ สำนวนที่เป็นผลจะใช้กับคอลเลกชันและอาร์เรย์อย่างเท่าเทียมกัน:

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}

เมื่อคุณเห็นเครื่องหมายจุดคู่ (:) ให้อ่านว่าเป็น "นิ้ว" ดังนั้นลูปด้านบนจะอ่านว่า“ สำหรับแต่ละองค์ประกอบอีในองค์ประกอบ” โปรดทราบว่าไม่มีการปรับประสิทธิภาพสำหรับการใช้ลูป for-each แม้กระทั่งสำหรับอาร์เรย์ ในความเป็นจริงมันอาจมีข้อได้เปรียบด้านประสิทธิภาพเล็กน้อยกว่าปกติสำหรับลูปในบางสถานการณ์เนื่องจากคำนวณขีด จำกัด ของดัชนีอาร์เรย์เพียงครั้งเดียว ในขณะที่คุณสามารถทำได้ด้วยมือ (รายการ 45) โปรแกรมเมอร์ไม่ได้ทำเช่นนั้นเสมอไป


48
มูลค่าการกล่าวขวัญว่าในสำหรับแต่ละวงมีวิธีที่จะเข้าถึงเคาน์เตอร์ดัชนี (เพราะมันไม่ได้อยู่) ไม่มี
basszero

ใช่ แต่ตัวนับนั้นตอนนี้สามารถมองเห็นได้นอกวง แน่นอนว่ามันเป็นการแก้ไขที่ง่าย แต่ก็มีไว้สำหรับแต่ละคน!
Indolering

74
มีการปรับประสิทธิภาพของการจัดสรรตัววนซ้ำ ฉันมีรหัสที่ขนานกันอย่างมากในวอลล์เปเปอร์สดของ Android ฉันเห็นว่าคนเก็บขยะบ้าไปแล้ว เป็นเพราะแต่ละลูปได้จัดสรรตัววนซ้ำชั่วคราวในเธรด (อายุสั้น) ที่แตกต่างกันจำนวนมากทำให้ตัวรวบรวมขยะทำงานมากมาย การสลับเป็นลูปที่ใช้ดัชนีเป็นประจำจะช่วยแก้ไขปัญหาได้
gsingh2011

1
@ gsingh2011 แต่สิ่งนี้ยังขึ้นอยู่กับว่าคุณใช้รายการเข้าถึงแบบสุ่มหรือไม่ การใช้การเข้าถึงดัชนีโดยใช้รายการเข้าถึงแบบไม่สุ่มจะแย่กว่าการใช้สำหรับแต่ละรายการที่มีรายการเข้าถึงแบบสุ่มฉันเดา หากคุณกำลังทำงานกับรายการอินเตอร์เฟสและไม่ทราบประเภทการนำไปใช้จริงคุณสามารถตรวจสอบว่ารายการดังกล่าวเป็นตัวอย่างของ (ใช้งาน) RandomAccess หรือไม่ถ้าคุณสนใจสิ่งนั้นมาก: docs.oracle.com/javase/8 /docs/api/java/util/RandomAccess.html
Puce

3
@ gsingh2011 เอกสาร Android ( developer.android.com/training/articles/perf-tips.html#Loops ) พูดถึงว่าคุณจะได้รับโทษประสิทธิภาพเมื่อใช้ foreach มากกว่า ArrayList เท่านั้นไม่ใช่คอลเล็กชันอื่น ฉันสงสัยว่าเป็นกรณีของคุณหรือเปล่า
Viccari

29

ลูปทั้งหมดนี้ทำอย่างเดียวกันฉันแค่ต้องการแสดงสิ่งเหล่านี้ก่อนที่จะโยนลงไปในสองเซ็นต์ของฉัน

ก่อนวิธีที่คลาสสิกของการวนซ้ำผ่านรายการ:

for (int i=0; i < strings.size(); i++) { /* do something using strings.get(i) */ }

ประการที่สองวิธีที่แนะนำเนื่องจากมีข้อผิดพลาดน้อยกว่า (คุณทำ "โอ๊ะโอผสมตัวแปร i และ j ในลูปเหล่านี้ในลูป" กี่ครั้ง?)

for (String s : strings) { /* do something using s */ }

ประการที่สามการปรับขนาดเล็กสำหรับลูป:

int size = strings.size();
for (int i = -1; ++i < size;) { /* do something using strings.get(i) */ }

ทีนี้สองเซนต์จริง ๆ : อย่างน้อยตอนที่ผมทดสอบพวกนี้อันที่สามนั้นเร็วที่สุดเมื่อนับมิลลิวินาทีว่ามันต้องใช้เวลานานเท่าใดสำหรับลูปแต่ละประเภทด้วยการใช้งานที่เรียบง่ายซ้ำสองสามล้านครั้ง - นี่ใช้ Java 5 กับ jre1.6u10 บน Windows ในกรณีที่ทุกคนสนใจ

ในขณะที่อย่างน้อยก็ดูเหมือนว่าจะเป็นอย่างที่สามคือเร็วที่สุดคุณควรถามตัวเองว่าคุณต้องการรับความเสี่ยงจากการใช้การเพิ่มประสิทธิภาพตาแมวนี้ทุกที่ในรหัสวนลูปของคุณตั้งแต่สิ่งที่ฉันเห็น โดยปกติแล้วจะใช้เวลาส่วนใหญ่ที่สุดในโปรแกรมจริง ๆ (หรือบางทีฉันก็แค่ทำงานผิดที่ใครจะรู้) และเช่นเดียวกับที่ฉันกล่าวถึงในข้ออ้างสำหรับ Java for-each loop (บางคนอ้างถึงมันเป็นIterator loopและอื่น ๆ ในฐานะfor-in loop ) คุณมีโอกาสน้อยที่จะพบข้อผิดพลาดโง่ ๆ เมื่อใช้มัน และก่อนที่จะถกเถียงกันว่าสิ่งนี้จะยิ่งเร็วกว่าตัวอื่น ๆ ได้อย่างไรโปรดจำไว้ว่า javac นั้นไม่ได้ปรับปรุงประสิทธิภาพ bytecode เลย (ดีเกือบทุกอย่างเลย) มันแค่รวบรวมมัน

หากคุณใช้การเพิ่มประสิทธิภาพแบบไมโครและ / หรือซอฟต์แวร์ของคุณใช้ลูปแบบวนซ้ำและเช่นนั้นคุณอาจสนใจประเภทลูปที่สาม เพียงจำไว้ว่าให้ทำการวัดค่าซอฟต์แวร์ของคุณทั้งก่อนและหลังการเปลี่ยนลูป


5
โปรดทราบว่าสำหรับลูปที่มี ++ i <= ขนาดคือ "ตาม 1" เช่นเมธอด get ภายในลูปจะถูกเรียกใช้สำหรับค่า 1, 2, 3 และอื่น ๆ
volley

15
วิธีที่ดีกว่าที่จะเขียนวงไมโครที่ดีที่สุดสำหรับ (int i = 0, size = strings.size (); ++ ฉัน <= ขนาด;) {} นี้เป็นที่นิยมเพราะมันช่วยลดขอบเขตของขนาด
Dónal

1
ไม่ใช่อันที่สามที่เริ่มต้นจาก i = 1 ในครั้งแรกที่มันผ่านลูปโดยข้ามองค์ประกอบแรก และมันเป็นห่วงสำหรับไม่จำเป็น int n = strings.length; ในขณะที่ (n -> 0) {System.out.println ("" + n + "" + สตริง [n]); }
Lassi Kinnunen

1
@ Dónเป็นรูปแบบวนรอบที่คิดถึงรูปแบบแรกและให้ IOOBE อันนี้ใช้งานได้: สำหรับ (int i = -1, size = list.size (); ++ i <size;)
Nathan Adams

1
"ลูปทั้งหมดเหล่านี้ทำสิ่งเดียวกัน" ไม่ถูกต้อง หนึ่งใช้เข้าถึงโดยสุ่มget(int), Iteratorการใช้งานอื่น พิจารณาLinkedListว่าประสิทธิภาพการทำงานของfor(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }แย่ลงมากเพราะทำget(int)n ครั้ง
Steve Kuo

13

โดยทั่วไปแล้วควรจะเลือกลูปสำหรับแต่ละลูป วิธี "รับ" อาจช้าลงหากการใช้งานรายการที่คุณใช้ไม่รองรับการเข้าถึงแบบสุ่ม ตัวอย่างเช่นหากใช้ LinkedList คุณจะต้องเสียค่าใช้จ่ายในการสำรวจเส้นทางในขณะที่วิธีการสำหรับแต่ละรายการจะใช้ตัววนซ้ำที่คอยติดตามตำแหน่งในรายการ ข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่างของแต่ละคนสำหรับวง

ฉันคิดว่าบทความนี้อยู่ที่นี่แล้ว: ตำแหน่งใหม่

ลิงก์ที่แสดงที่นี่เสียชีวิต


12

ผลกระทบด้านประสิทธิภาพนั้นแทบไม่มีนัยสำคัญ แต่ก็ไม่เป็นศูนย์ หากคุณดูที่ JavaDoc ของRandomAccessอินเตอร์เฟส:

ตามกฎง่ายๆการใช้งานรายการควรใช้อินเทอร์เฟซนี้ถ้าสำหรับอินสแตนซ์ทั่วไปของคลาสลูปนี้:

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);

วิ่งเร็วกว่าลูปนี้:

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();

และสำหรับแต่ละลูปจะใช้เวอร์ชันพร้อมตัววนซ้ำดังนั้นArrayListตัวอย่างเช่นสำหรับลูปแต่ละวงจะไม่เร็วที่สุด


จริงเหรอ แม้แต่อันเดียวกับอาร์เรย์? ฉันอ่านที่นี่stackoverflow.com/questions/1006395/…ว่าไม่เกี่ยวข้องกับตัววนซ้ำ
Ondra Žižka

@ OndraŽižka: for-each loop ใช้ตัววนซ้ำเมื่อวนรอบIterables แต่ไม่ใช่อาร์เรย์ สำหรับอาร์เรย์จะใช้ for-loop พร้อมกับตัวแปรดัชนี มีข้อมูลเกี่ยวกับเรื่องนี้ในเป็นJLS
Lii

ใช่คุณต้องสร้างเป็นอาร์เรย์ด้วย toArray ก่อนโดยมีค่าใช้จ่าย
Lassi Kinnunen

6

น่าเสียดายที่มีความแตกต่าง

หากคุณดูที่โค้ดไบต์ที่สร้างขึ้นสำหรับลูปทั้งสองชนิดมันจะแตกต่างกัน

นี่คือตัวอย่างจากซอร์สโค้ด Log4j

ใน /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java เรามีคลาสภายในแบบคงที่ชื่อ Log4jMarker ซึ่งกำหนด:

    /*
     * Called from add while synchronized.
     */
    private static boolean contains(final Marker parent, final Marker... localParents) {
        //noinspection ForLoopReplaceableByForEach
        for (final Marker marker : localParents) {
            if (marker == parent) {
                return true;
            }
        }
        return false;
    }

ด้วยลูปมาตรฐาน:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: iconst_0
       1: istore_2
       2: aload_1
       3: arraylength
       4: istore_3
       5: iload_2
       6: iload_3
       7: if_icmpge     29
      10: aload_1
      11: iload_2
      12: aaload
      13: astore        4
      15: aload         4
      17: aload_0
      18: if_acmpne     23
      21: iconst_1
      22: ireturn
      23: iinc          2, 1
      26: goto          5
      29: iconst_0
      30: ireturn

ด้วยสำหรับแต่ละ:

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: aload_1
       1: astore_2
       2: aload_2
       3: arraylength
       4: istore_3
       5: iconst_0
       6: istore        4
       8: iload         4
      10: iload_3
      11: if_icmpge     34
      14: aload_2
      15: iload         4
      17: aaload
      18: astore        5
      20: aload         5
      22: aload_0
      23: if_acmpne     28
      26: iconst_1
      27: ireturn
      28: iinc          4, 1
      31: goto          8
      34: iconst_0
      35: ireturn

เกิดอะไรขึ้นกับ Oracle นั่น?

ฉันได้ลองกับ Java 7 และ 8 บน Windows 7


7
สำหรับผู้ที่พยายามอ่านการถอดแยกชิ้นส่วนผลลัพธ์สุทธิคือโค้ดที่สร้างขึ้นภายในลูปนั้นเหมือนกัน แต่สำหรับแต่ละเซ็ตอัพดูเหมือนจะสร้างตัวแปรชั่วคราวเพิ่มเติมที่มีการอ้างอิงถึงอาร์กิวเมนต์ที่สอง หากมีการลงทะเบียนตัวแปรพิเศษที่ซ่อนอยู่ แต่พารามิเตอร์นั้นไม่ได้อยู่ในระหว่างการสร้างโค้ดดังนั้น for-each จะเร็วขึ้น หากพารามิเตอร์ถูกลงทะเบียนในตัวอย่างสำหรับ (;;) เวลาดำเนินการจะเหมือนกัน ต้องเปรียบเทียบหรือไม่
Robin Davies

4

ควรใช้ตัววนซ้ำแทนการทำดัชนี นี่เป็นเพราะตัววนซ้ำน่าจะเหมาะสมที่สุดสำหรับการใช้งานรายการในขณะที่การทำดัชนี (การเรียกรับ) อาจไม่ใช่ ตัวอย่างเช่น LinkedList เป็นรายการ แต่การทำดัชนีผ่านองค์ประกอบจะช้ากว่าการวนซ้ำโดยใช้ตัววนซ้ำ


10
ฉันคิดว่าไม่มีสิ่งเช่น "เสมอ" ในการเพิ่มประสิทธิภาพประสิทธิภาพ.)
515 eckes

4

foreach ทำให้เจตนาของโค้ดของคุณชัดเจนขึ้นและเป็นที่ต้องการมากกว่าการปรับปรุงความเร็วเล็กน้อย - ถ้ามี

เมื่อใดก็ตามที่ฉันเห็นลูปที่มีการจัดทำดัชนีฉันต้องแยกมันออกไปอีกหน่อยเพื่อให้แน่ใจว่ามันทำในสิ่งที่ฉันคิดว่ามันเป็นเช่นมันเริ่มจากศูนย์มันรวมหรือแยกจุดสิ้นสุดเป็นต้นหรือไม่?

เวลาส่วนใหญ่ของฉันดูเหมือนว่าจะใช้รหัสการอ่าน (ที่ฉันเขียนหรือคนอื่นเขียน) และความคมชัดมักจะสำคัญกว่าประสิทธิภาพ มันง่ายที่จะยกเลิกประสิทธิภาพการทำงานในวันนี้เพราะ Hotspot ทำหน้าที่ได้อย่างยอดเยี่ยม


4

รหัสต่อไปนี้:

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

interface Function<T> {
    long perform(T parameter, long x);
}

class MyArray<T> {

    T[] array;
    long x;

    public MyArray(int size, Class<T> type, long x) {
        array = (T[]) Array.newInstance(type, size);
        this.x = x;
    }

    public void forEach(Function<T> function) {
        for (T element : array) {
            x = function.perform(element, x);
        }
    }
}

class Compute {
    int factor;
    final long constant;

    public Compute(int factor, long constant) {
        this.factor = factor;
        this.constant = constant;
    }

    public long compute(long parameter, long x) {
        return x * factor + parameter + constant;
    }
}

public class Main {

    public static void main(String[] args) {
        List<Long> numbers = new ArrayList<Long>(50000000);
        for (int i = 0; i < 50000000; i++) {
            numbers.add(i * i + 5L);
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.size(); i++) {
            x += x * 7 + numbers.get(i) + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        numbers = null;
        MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return x * 8 + parameter + 5L;
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
        myArray = null;
        myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return new Compute(8, 5).compute(parameter, x);
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
    }
}

ให้เอาต์พุตต่อไปนี้บนระบบของฉัน:

224
-699150247503735895
221
-699150247503735895
220
-699150247503735895
219
-699150247503735895

ฉันใช้ Ubuntu 12.10 alpha กับ OracleJDK 1.7 update 6

โดยทั่วไปแล้ว HotSpot จะเพิ่มประสิทธิภาพของการอ้อมค้อมและการดำเนินการ reduntant ง่าย ๆ ดังนั้นโดยทั่วไปคุณไม่ควรกังวลเกี่ยวกับสิ่งเหล่านี้เว้นแต่จะมีพวกเขาจำนวนมากในสถานการณ์ที่ไม่เหมาะสมหรือพวกมันซ้อนกันอย่างหนัก

ในทางกลับกันการทำดัชนี get on LinkedList นั้นช้ากว่าการเรียกตัวถัดไปบนตัววนซ้ำสำหรับ LinkedList ดังนั้นคุณสามารถหลีกเลี่ยงการเข้าชมที่เกิดขึ้นในขณะที่ยังคงสามารถอ่านได้เมื่อคุณใช้ตัววนซ้ำ (โดยชัดแจ้งหรือโดยนัย


3

แม้ว่าจะมีบางอย่างเช่น ArrayList หรือ Vector ซึ่ง "get" เป็นการค้นหาอาร์เรย์อย่างง่าย แต่การวนซ้ำครั้งที่สองยังคงมีโอเวอร์เฮดเพิ่มเติมซึ่งครั้งแรกไม่มี ฉันคาดว่ามันช้ากว่าครั้งแรกเล็กน้อย


การวนซ้ำแรกจะต้องได้รับแต่ละองค์ประกอบด้วย มันสร้างตัววนซ้ำเบื้องหลังเพื่อทำสิ่งนี้ พวกมันเทียบเท่ากันจริงๆ
Bill the Lizard

การคิดในแง่ของ C ตัววนซ้ำสามารถเพิ่มพอยน์เตอร์ได้ แต่การได้รับจะต้องคูณค่าของ i ด้วยความกว้างของตัวชี้ในแต่ละครั้ง
พอลทอมบลิน

ขึ้นอยู่กับประเภทรายการที่คุณใช้ ฉันคิดว่าคุณพูดถูก แต่การใช้ get จะไม่เร็วและบางครั้งก็ช้าลง
Bill the Lizard

3

วิธีเดียวที่จะทราบว่าเป็นมาตรฐานมันและแม้ที่ไม่ได้ง่ายอย่างที่มันอาจจะฟังดู คอมไพเลอร์ JIT สามารถทำสิ่งที่ไม่คาดคิดกับโค้ดของคุณ


3

นี่คือการวิเคราะห์สั้น ๆ เกี่ยวกับความแตกต่างของทีมพัฒนา Android:

https://www.youtube.com/watch?v=MZOf3pOAM6A

ผลที่ตามมาก็คือว่ามีคือความแตกต่างและอยู่ในสภาพแวดล้อมที่ จำกัด มากกับรายการที่มีขนาดใหญ่มากมันอาจจะเป็นความแตกต่างที่เห็นได้ชัด ในการทดสอบของพวกเขาแต่ละวงใช้เวลานานเป็นสองเท่า อย่างไรก็ตามการทดสอบของพวกเขาอยู่ในรายการอาร์เรย์ของจำนวนเต็ม 400,000 ความแตกต่างที่เกิดขึ้นจริงต่อองค์ประกอบในอาร์เรย์ 6 microseconds ฉันยังไม่ได้ทดสอบและพวกเขาไม่ได้พูด แต่ฉันคาดหวังว่าความแตกต่างจะใหญ่กว่าการใช้วัตถุแทนที่จะเป็นแบบดั้งเดิม แต่ก็ยังเว้นแต่ว่าคุณกำลังสร้างรหัสห้องสมุดที่คุณไม่ทราบขนาดของสิ่งที่คุณจะถาม เพื่อย้ำมากกว่าฉันคิดว่าความแตกต่างนั้นไม่คุ้มกับการเครียด


2

โดยใช้ชื่อตัวแปรobjectArrayList, java.util.ArrayListฉันคิดว่าเป็นตัวอย่างของ ในกรณีนั้นความแตกต่างด้านประสิทธิภาพจะไม่สามารถสังเกตเห็นได้

ในทางกลับกันหากเป็นตัวอย่างของjava.util.LinkedListวิธีการที่สองจะช้ากว่ามากเนื่องจากเป็นการดำเนินList#get(int)การ O (n)

ดังนั้นวิธีแรกจึงเป็นที่ต้องการเสมอยกเว้นว่าต้องการดัชนีโดยตรรกะในการวนซ้ำ


1
1. for(Object o: objectArrayList){
    o.DoSomthing();
}
and

2. for(int i=0; i<objectArrayList.size(); i++){
    objectArrayList.get(i).DoSomthing();
}

ทั้งสองทำสิ่งเดียวกัน แต่เพื่อความสะดวกและปลอดภัยในการเขียนโปรแกรมใช้สำหรับแต่ละคนมีความเป็นไปได้ที่จะเกิดข้อผิดพลาดในวิธีที่ 2


1

มันแปลกที่ไม่มีใครพูดถึงความชัดเจน - foreach จัดสรรหน่วยความจำ (ในรูปแบบของตัววนซ้ำ) ในขณะที่ปกติสำหรับลูปไม่ได้จัดสรรหน่วยความจำใด ๆ สำหรับเกมบน Android นี่เป็นปัญหาเพราะหมายความว่าตัวรวบรวมขยะจะทำงานเป็นระยะ ในเกมคุณไม่ต้องการให้ตัวรวบรวมขยะทำงาน ... เคย ดังนั้นอย่าใช้วิธีลูป foreach ในวิธีการวาด (หรือเรนเดอร์) ของคุณ


1

คำตอบที่ได้รับการยอมรับตอบคำถามนอกเหนือจากกรณีพิเศษของ ArrayList ...

เนื่องจากนักพัฒนาส่วนใหญ่พึ่งพา ArrayList (อย่างน้อยฉันก็เชื่อเช่นนั้น)

ดังนั้นฉันจำเป็นต้องเพิ่มคำตอบที่ถูกต้องที่นี่

ส่งตรงจากเอกสารของนักพัฒนาซอฟต์แวร์: -

Enhanced for loop (บางครั้งเรียกว่า "for-each" loop) สามารถใช้สำหรับคอลเลกชันที่ใช้ Iterable Interface และ Array ด้วยคอลเล็กชันตัววนซ้ำจะถูกจัดสรรเพื่อทำการเรียกใช้อินเตอร์เฟสไปยัง hasNext () และถัดไป () ด้วย ArrayList การวนซ้ำที่เขียนด้วยมือจะเร็วกว่าประมาณ 3 เท่า (มีหรือไม่มี JIT) แต่สำหรับคอลเลกชันอื่น ๆ การปรับปรุงสำหรับไวยากรณ์ของลูปจะเทียบเท่ากับการใช้ตัววนซ้ำอย่างชัดเจน

มีหลายทางเลือกสำหรับการวนซ้ำผ่านอาร์เรย์:

static class Foo {
    int mSplat;
}

Foo[] mArray = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < mArray.length; ++i) {
        sum += mArray[i].mSplat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].mSplat;
    }
}

public void two() {
    int sum = 0;
    for (Foo a : mArray) {
        sum += a.mSplat;
    }
}

ศูนย์ () ช้าที่สุดเนื่องจาก JIT ยังไม่สามารถปรับค่าใช้จ่ายในการรับความยาวของอาร์เรย์ได้หนึ่งครั้งสำหรับการวนซ้ำทุกครั้งผ่านลูป

หนึ่ง () เร็วขึ้น มันดึงทุกอย่างออกเป็นตัวแปรท้องถิ่นหลีกเลี่ยงการค้นหา เฉพาะความยาวของอาเรย์เท่านั้นที่ให้ประโยชน์ด้านประสิทธิภาพ

สอง () เร็วที่สุดสำหรับอุปกรณ์ที่ไม่มี JIT และแยกไม่ออกจากหนึ่ง () สำหรับอุปกรณ์ที่มี JIT มันใช้ Enhanced สำหรับไวยากรณ์วนลูปที่แนะนำในรุ่น 1.5 ของภาษาการเขียนโปรแกรม Java

ดังนั้นคุณควรใช้ขั้นสูงสำหรับลูปเป็นค่าเริ่มต้น แต่ให้พิจารณาลูปนับที่เขียนด้วยมือสำหรับการทำซ้ำ ArrayList ที่สำคัญต่อประสิทธิภาพ


-2

ใช่ตัวแปรจะเร็วกว่ากว่าปกติ for-eachindex-based-for-loop

for-eachiteratorการใช้งานที่แตกต่างกัน ดังนั้นการforเคลื่อนที่เร็วกว่าลูปปกติซึ่งเป็นดัชนี
นี้เป็นเพราะiteratorจะเหมาะสำหรับ traversing เพราะมันจะชี้ไปก่อนองค์ประกอบถัดไปและหลังองค์ประกอบก่อนหน้านี้ หนึ่งในเหตุผลที่เป็นindex-based-for-loopไปได้ช้าคือมันต้องคำนวณและย้ายไปยังตำแหน่งองค์ประกอบในแต่ละครั้งiteratorที่ไม่ได้อยู่กับ


-3
public class FirstJavaProgram {

    public static void main(String[] args) 
    {
        int a[]={1,2,3,45,6,6};

// Method 1: this is simple way to print array 

        for(int i=0;i<a.length;i++) 
        { 
            System.out.print(a[i]+" ");
        }

// Method 2: Enhanced For loop

        for(int i:a)
        {
            System.out.print(i+" ");
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.