كيف تعمل حلقة Java لكل حلقة؟



10 Answers

إن البنية لكل منها صالحة أيضًا للصفائف. على سبيل المثال

String[] fruits = new String[] { "Orange", "Apple", "Pear", "Strawberry" };

for (String fruit : fruits) {
    // fruit is an element of the `fruits` array.
}

وهو ما يعادل في الأساس

for (int i = 0; i < fruits.length; i++) {
    String fruit = fruits[i];
    // fruit is an element of the `fruits` array.
}

لذا ، الملخص الشامل:
[nsayer] فيما يلي الشكل الأطول لما يحدث:

for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
  String item = i.next();
  System.out.println(item);
}

لاحظ أنه إذا كنت بحاجة إلى استخدام i.remove ()؛ في الحلقة ، أو الوصول إلى التكرار الفعلي بطريقة ما ، لا يمكنك استخدام صيغة (:) ، حيث يتم استنتاج Iterator الفعلي فقط.

[دينيس بوينو]

إنه يتضمن إجابة nsayer ، لكن تجدر الإشارة إلى أن صيغة OP الخاصة بـ (..) ستعمل عندما يكون "someList" أي شيء يقوم بتنفيذ java.lang.Iterable - ليس من الضروري أن تكون قائمة ، أو مجموعة من java.util. حتى الأنواع الخاصة بك ، لذلك ، يمكن استخدامها مع هذا النحو.

Question

يعتبر:

List<String> someList = new ArrayList<String>();
// add "monkey", "donkey", "skeleton key" to someList
for (String item : someList) {
    System.out.println(item);
}

ما هو الشكل المكافئ للحلقة مثل دون استخدام لكل بناء جملة؟




docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html ، المضافة في Java 5 (وتسمى أيضًا "المحسن للحلقة") ، تعادل استخدام java.util.Iterator - وهو السكر النحوي لنفس الشيء. لذلك ، عند قراءة كل عنصر ، واحدًا تلو الآخر ، يجب اختيار foreach دائمًا على مُكرّر ، نظرًا لأنه أكثر ملاءمة ومختصرًا.

foreach

for(int i : intList) {
   System.out.println("An element in the list: " + i);
}

مكرر

Iterator<Integer> intItr = intList.iterator();
while(intItr.hasNext()) {
   System.out.println("An element in the list: " + intItr.next());
}

هناك حالات حيث يجب عليك استخدام Iterator مباشرة. على سبيل المثال ، يمكن أن تؤدي محاولة حذف عنصر أثناء استخدام foreach (will؟) إلى ConcurrentModificationException .

foreach مقابل. for : الاختلافات الأساسية

الفرق العملي الوحيد بين for foreach هو أنه في حالة الكائنات القابلة للفهرسة ، لا يمكنك الوصول إلى الفهرس. مثال عند الحاجة for الحلقة الأساسية:

for(int i = 0; i < array.length; i++) {
   if(i < 5) {
      // Do something special
   }  else {
      // Do other stuff
   }
}

على الرغم من أنه يمكنك إنشاء متغير int index يدويًا مع foreach ،

int idx = -1;
for(int i : intArray) {
   idx++;
   ...
}

لا يُنصح بذلك ، نظرًا لأن variable-scope ليس مثاليًا ، والحلقة الأساسية هي ببساطة التنسيق القياسي والمتوقع لحالة الاستخدام هذه.

foreach vs. for : Performance

عند الوصول إلى المجموعات ، يكون foreach أسرع بشكل ملحوظ من الوصول الأساسي لصفيف الحلقة. ومع ذلك ، عند الوصول إلى المصفوفات - على الأقل مع المصفوفات البدائية والمغلفة - يكون الوصول عبر الفهارس أسرع بشكل كبير.

توقيت الفرق بين المكرر ووصول الفهرس لصفيف intrays البدائي

تكون الفهارس أسرع بنسبة 23 إلى 40 بالمائة من المتكررات عند الوصول إلى صفائف int أو Integer . هنا هو الإخراج من فئة الاختبار في الجزء السفلي من هذا المنصب ، والذي جمع الأرقام في مجموعة بدائية-عنصر 100 عنصر (A هو تكرار ، B هو الفهرس):

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 358,597,622 nanoseconds
Test B: 269,167,681 nanoseconds
B faster by 89,429,941 nanoseconds (24.438799231635727% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 377,461,823 nanoseconds
Test B: 278,694,271 nanoseconds
B faster by 98,767,552 nanoseconds (25.666236154695838% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 288,953,495 nanoseconds
Test B: 207,050,523 nanoseconds
B faster by 81,902,972 nanoseconds (27.844689860906513% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,373,765 nanoseconds
Test B: 283,813,875 nanoseconds
B faster by 91,559,890 nanoseconds (23.891659337194227% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 375,790,818 nanoseconds
Test B: 220,770,915 nanoseconds
B faster by 155,019,903 nanoseconds (40.75164734599769% faster)

[C:\java_code\]java TimeIteratorVsIndexIntArray 1000000
Test A: 326,373,762 nanoseconds
Test B: 202,555,566 nanoseconds
B faster by 123,818,196 nanoseconds (37.437545972215744% faster)

أنا أيضا ركض هذا لمجموعة Integer ، والفهرس لا يزال هو الفائز الواضح ، ولكن فقط بين 18 و 25 في المئة أسرع.

للمجموعات ، تكون التكرارات أسرع من الفهارس

أما بالنسبة إلى List Integers ، فإن المتكررين هم الفائزون الواضحون. ما عليك سوى تغيير صفيف int في صف الاختبار إلى:

List<Integer> intList = Arrays.asList(new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100});

وقم بإجراء التغييرات اللازمة على وظيفة الاختبار ( int[] إلى List<Integer> ، length إلى size() ، وما إلى ذلك):

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,429,929,976 nanoseconds
Test B: 5,262,782,488 nanoseconds
A faster by 1,832,852,512 nanoseconds (34.326681820485675% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,907,391,427 nanoseconds
Test B: 3,957,718,459 nanoseconds
A faster by 1,050,327,032 nanoseconds (26.038700083921256% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,566,004,688 nanoseconds
Test B: 4,221,746,521 nanoseconds
A faster by 1,655,741,833 nanoseconds (38.71935684115413% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 2,770,945,276 nanoseconds
Test B: 3,829,077,158 nanoseconds
A faster by 1,058,131,882 nanoseconds (27.134122749113843% faster)

[C:\java_code\]java TimeIteratorVsIndexIntegerList 1000000
Test A: 3,467,474,055 nanoseconds
Test B: 5,183,149,104 nanoseconds
A faster by 1,715,675,049 nanoseconds (32.60101667104192% faster)

[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,439,983,933 nanoseconds
Test B: 3,509,530,312 nanoseconds
A faster by 69,546,379 nanoseconds (1.4816434912159906% faster)

[C:\java_code\]java TimeIteratorVsIndexIntList 1000000
Test A: 3,451,101,466 nanoseconds
Test B: 5,057,979,210 nanoseconds
A faster by 1,606,877,744 nanoseconds (31.269164666060377% faster)

في اختبار واحد يكاد يكونوا متكافئين ، ولكن مع المجموعات ، يفوز المحرر.

* هذه المشاركة مبنية على إجابتين كتبت على :

بعض المعلومات الإضافية : أيهما أكثر كفاءة ، أو لكل حلقة ، أو مكرر؟

فئة الاختبار الكامل

لقد أنشأت هذا الفصل بين الفترات الزمنية التي يستغرقها وأحدث أي شيئين بعد قراءة هذا السؤال على :

import  java.text.NumberFormat;
import  java.util.Locale;

/**
   &lt;P&gt;{@code java TimeIteratorVsIndexIntArray 1000000}&lt;/P&gt;

   @see  &lt;CODE&gt;&lt;A HREF=&quot;https://.com/questions/180158/how-do-i-time-a-methods-execution-in-java&quot;&gt;https://.com/questions/180158/how-do-i-time-a-methods-execution-in-java&lt;/A&gt;&lt;/CODE&gt;
 **/
public class TimeIteratorVsIndexIntArray {

    public static final NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);

    public static final void main(String[] tryCount_inParamIdx0) {
        int testCount;

        // Get try-count from a command-line parameter
        try {
           testCount = Integer.parseInt(tryCount_inParamIdx0[0]);
        }
        catch(ArrayIndexOutOfBoundsException | NumberFormatException x) {
           throw  new IllegalArgumentException("Missing or invalid command line parameter: The number of testCount for each test. " + x);
        }

        //Test proper...START
        int[] intArray = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100};

        long lStart = System.nanoTime();
        for(int i = 0; i < testCount; i++) {
           testIterator(intArray);
        }

        long lADuration = outputGetNanoDuration("A", lStart);

        lStart = System.nanoTime();
        for(int i = 0; i < testCount; i++) {
           testFor(intArray);
        }

        long lBDuration = outputGetNanoDuration("B", lStart);

        outputGetABTestNanoDifference(lADuration, lBDuration, "A", "B");
    }

    private static final void testIterator(int[] int_array) {
       int total = 0;
       for(int i = 0; i < int_array.length; i++) {
          total += int_array[i];
       }
    }

    private static final void testFor(int[] int_array) {
       int total = 0;
       for(int i : int_array) {
          total += i;
       }
    }
    //Test proper...END

    //Timer testing utilities...START
    public static final long outputGetNanoDuration(String s_testName, long l_nanoStart) {
        long lDuration = System.nanoTime() - l_nanoStart;
        System.out.println("Test " + s_testName + ": " + nf.format(lDuration) + " nanoseconds");
        return  lDuration;
    }

    public static final long outputGetABTestNanoDifference(long l_aDuration, long l_bDuration, String s_aTestName, String s_bTestName) {
        long lDiff = -1;
        double dPct = -1.0;
        String sFaster = null;
        if(l_aDuration > l_bDuration) {
            lDiff = l_aDuration - l_bDuration;
            dPct = 100.00 - (l_bDuration * 100.0 / l_aDuration + 0.5);
            sFaster = "B";
        }
        else {
            lDiff = l_bDuration - l_aDuration;
            dPct = 100.00 - (l_aDuration * 100.0 / l_bDuration + 0.5);
            sFaster = "A";
        }
        System.out.println(sFaster + " faster by " + nf.format(lDiff) + " nanoseconds (" + dPct + "% faster)");
        return  lDiff;
   }

   //Timer testing utilities...END

}



لاحظ أيضًا أن استخدام طريقة "foreach" في السؤال الأصلي له بعض القيود ، مثل عدم القدرة على إزالة عناصر من القائمة أثناء التكرار.

من السهل قراءة حلقة for-new الجديدة وإزالة الحاجة إلى أداة فصل منفصلة ، ولكنها لا يمكن استخدامها إلا في عمليات إعادة قراءة للقراءة فقط.




كما قال العديد من الإجابات الجيدة ، يجب على كائن تنفيذ Iterable interface إذا كان يريد استخدام حلقة for-each .

سأقوم بنشر مثال بسيط وحاول أن أشرح بطريقة مختلفة كيف تعمل حلقة for-each .

مثال الحلقة for-each :

public class ForEachTest {

    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();
        list.add("111");
        list.add("222");

        for (String str : list) {
            System.out.println(str);
        }
    }
}

ثم ، إذا استخدمنا javap هذا الصف ، فسوف نحصل على عينة bytecode:

public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: new           #16                 // class java/util/ArrayList
         3: dup
         4: invokespecial #18                 // Method java/util/ArrayList."<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #19                 // String 111
        11: invokeinterface #21,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        16: pop
        17: aload_1
        18: ldc           #27                 // String 222
        20: invokeinterface #21,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        25: pop
        26: aload_1
        27: invokeinterface #29,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;

كما نرى من السطر الأخير من العينة ، سيقوم المحول البرمجي تلقائيًا بتحويل استخدام for-each كلمة رئيسية إلى استخدام Iterator في وقت التحويل البرمجي. قد يفسر هذا سبب كائن ، الذي لا يقوم بتطبيق Iterable interface ، سيؤدي إلى Exception عند محاولة استخدام الحلقة for-each .




كما هو محدد في JLS for-each يمكن أن يكون لكل حلقة نموذجين:

  1. إذا كان نوع Expression هو نوع فرعي من Iterable فإن الترجمة هي كما يلي:

    List<String> someList = new ArrayList<String>();
    someList.add("Apple");
    someList.add("Ball");
    for (String item : someList) {
        System.out.println(item);
    }
    
    // IS TRANSLATED TO:
    
    for(Iterator<String> stringIterator = someList.iterator(); stringIterator.hasNext(); ) {
        String item = stringIterator.next();
        System.out.println(item);
    }
    
  2. إذا كان لـ Expression بالضرورة نوع صفيف T[] فيمكنك:

    String[] someArray = new String[2];
    someArray[0] = "Apple";
    someArray[1] = "Ball";
    
    for(String item2 : someArray) {
        System.out.println(item2);
    }
    
    // IS TRANSLATED TO:
    for (int i = 0; i < someArray.length; i++) {
        String item2 = someArray[i];
        System.out.println(item2);
    }
    

لقد أدخلت Java 8 تدفقات تؤدي بشكل أفضل. يمكننا استخدامها على النحو التالي:

someList.stream().forEach(System.out::println);
Arrays.stream(someArray).forEach(System.out::println);



public static Boolean Add_Tag(int totalsize)
{ List<String> fullst = new ArrayList<String>();
            for(int k=0;k<totalsize;k++)
            {
              fullst.addAll();
            }
}



for (Iterator<String> itr = someList.iterator(); itr.hasNext(); ) {
   String item = itr.next();
   System.out.println(item);
}



سيبدو شيء هكذا. جد قوي

for (Iterator<String> i = someList.iterator(); i.hasNext(); )
        System.out.println(i.next());

هناك كتابة جيدة على كل في وثائق الشمس .




في ميزات Java 8 ، يمكنك استخدام هذا:

List<String> messages = Arrays.asList("First", "Second", "Third");

void forTest(){
    messages.forEach(System.out::println);
}

انتاج |

First
Second
Third



هذا يبدو مجنونا ولكن يا يعمل

List<String> someList = new ArrayList<>(); //has content
someList.forEach(System.out::println);

هذا يعمل. سحر




سيسمح إنشاء الحلقة Java "for-each" بالتكرار على نوعين من الكائنات:

  • T[] (صفائف من أي نوع)
  • java.lang.Iterable<T>

تحتوي واجهة Iterable<T> على أسلوب واحد فقط: Iterator<T> iterator() . هذا يعمل على كائنات من نوع Collection<T> لأن واجهة Collection<T> تمتد إلى Iterable<T> .




Related