有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

foreach Java“for each”循环是如何工作的?

考虑:

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

如果不对每个语法使用,那么等效的for循环会是什么样子


共 (6) 个答案

  1. # 1 楼答案

    Java中的for-each循环使用底层迭代器机制。因此,它与以下内容相同:

    Iterator<String> iterator = someList.iterator();
    
    while (iterator.hasNext()) {
      String item = iterator.next();
      System.out.println(item);
    }
    
  2. # 2 楼答案

    这里有一个答案,它并不假定您了解Java迭代器。它不太精确,但对教育很有用

    编程时,我们通常编写如下代码:

    char[] grades = ....
    for(int i = 0; i < grades.length; i++) {   // for i goes from 0 to grades.length
        System.out.print(grades[i]);           // Print grades[i]
    }
    

    foreach语法允许以更自然、更少语法噪音的方式编写此公共模式

    for(char grade : grades) {   // foreach grade in grades
        System.out.print(grade); // print that grade
    }
    

    此外,此语法对于不支持数组索引但实现Java Iterable接口的列表或集合等对象有效

  3. # 3 楼答案

    Java 5中添加的for-each loop(也称为“增强的for循环”),相当于使用^{}——它是相同事物的语法糖。因此,在逐项按顺序读取每个元素时,应始终在迭代器上选择一个for-each,因为它更方便、更简洁

    对于每个

    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。例如,在为每个can(will?)使用时尝试删除一个元素结果是ConcurrentModificationException

    For each与For循环:基本区别

    for循环和for each之间唯一实际的区别是,对于可索引对象,您无权访问索引。需要基本for循环时的示例:

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

    尽管您可以手动创建一个单独的索引int变量

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

    。。。不建议这样做,因为variable-scope并不理想,基本for循环只是这个用例的标准和预期格式

    For each与For循环:性能

    访问集合时,for each比基本for循环的数组访问权限大significantly faster。然而,当访问数组时——至少在使用原语和包装器数组时——通过索引进行访问的速度要快得多

    对基本int数组的迭代器和索引访问之间的差异进行计时

    在访问intInteger数组时,索引比迭代器快23-40。这是本文底部测试类的输出,它对100个元素的原始int数组中的数字求和(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>lengthsize(),等等):

    [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)
    

    在一个测试中,它们几乎是等价的,但对于集合,迭代器获胜

    *本文基于我对堆栈溢出的两个回答:

    更多信息:Which is more efficient, a for-each loop, or an iterator?

    完整的测试等级

    我在阅读了堆栈溢出上的this question之后,创建了这个比较类来做任何两件事所需的时间:

    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://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java&quot;&gt;https://stackoverflow.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
    
    }
    
  4. # 4 楼答案

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

    请注意,如果需要在循环中使用i.remove();,或者以某种方式访问实际的迭代器,则不能使用for ( : )习惯用法,因为实际的迭代器只是推断出来的

    正如Denis Bueno所指出的,此代码适用于实现^{} interface的任何对象

    此外,如果for (:)习惯用法的右侧是array而不是Iterable对象,则内部代码使用一个int索引计数器并检查array.length。见Java Language Specification

  5. # 5 楼答案

    JLS中所定义,每个循环可以有两种形式:

    1. 如果表达式的类型是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. 如果表达式必须具有数组类型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);
      }
      

    Java8引入了流,这些流在使用下降大小数据集时通常表现得更好。我们可以将其用作:

    someList.stream().forEach(System.out::println);
    Arrays.stream(someArray).forEach(System.out::println);
    
  6. # 6 楼答案

    每个的构造也适用于数组。e、 g

    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);
    }
    

    Note that if you need to use i.remove(); in your loop, or access the actual iterator in some way, you cannot use the for( : ) idiom, since the actual Iterator is merely inferred.

    [Denis Bueno]

    It's implied by nsayer's answer, but it's worth noting that the OP's for(..) syntax will work when "someList" is anything that implements java.lang.Iterable -- it doesn't have to be a list, or some collection from java.util. Even your own types, therefore, can be used with this syntax.