【问题标题】:Will this code be evaluated on every iteration?是否会在每次迭代时评估此代码?
【发布时间】:2009-10-09 13:22:18
【问题描述】:

我有这个 for-each-loop:

for (Component c : container.getComponents()) {
    // Loop code
}

每次迭代都会调用getComponents 吗?在查看之前调用getComponents 并且只对缓存数组起作用是否有意义?

【问题讨论】:

    标签: java performance loops


    【解决方案1】:

    不,getComponents() 只会被调用一次。

    相当于:

    for (Iterator<Component> iterator = container.getComponents().getIterator();
         iterator.hasNext(); )
    {
        Component c = iterator.next();
        ...
    }
    

    来自section 14.14.2 of the Java Language Specification

    增强的 for 语句具有以下形式:

    EnhancedForStatement:

    for ( VariableModifiersopt Type Identifier: Expression) Statement
    

    表达式必须是 Iterable 类型,否则它必须是数组类型(第 10.1 节),> 否则会发生编译时错误。 在增强 for 的 FormalParameter 部分中声明的局部变量的范围 statement (§14.14) 是包含的 Statement

    增强for语句的含义通过翻译成基本的for来给出 声明。

    如果 Expression 的类型是 Iterable 的子类型,那么让 I 成为 表达式Expression.iterator()。增强的 for 语句相当于一个基本的 for声明表格:

    for (I #i = Expression.iterator(); #i.hasNext(); ) {
       VariableModifiersopt Type Identifier = #i.next();
       Statement
    }
    

    【讨论】:

    • Jon,编译器如何保证 getComponents() 没有副作用并且不会在每次迭代中产生不同的东西? (例如,如果另一个线程更改了组件?)。
    • 它没有...但是上面的翻译基本上是它定义的。我会尝试找到 JLS 参考。
    • 我的错。我误读了源代码。我认为 OP 是在询问 for 循环中的控制条件是否被重新评估。我没有注意到这是一个 for-each。
    【解决方案2】:

    getComponents 将被调用一次,并返回一个迭代器。然后将使用 next() 和 hasNext() 方法调用此迭代器。

    更新这里有更多细节试图让 Skeet Jon Skeet 在这个答案中脱颖而出。

    以下程序显示了如何调用iterator 一次,即使集合中有三个项目。

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>() {
            public java.util.Iterator<String> iterator() {
                System.out.println("iterator() called");
                return super.iterator();
            };
        };
        list.add("a");
        list.add("b");
        list.add("c");
    
        for (String s : list) {
            System.out.println(s);
        }
    }
    

    输出:

    iterator() called
    a
    b
    c
    

    如果您通过 Java 反编译器运行它,您会发现以下代码:

        String s;
        for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(s))
            s = (String)iterator.next();
    

    另外,由于我们从JLS 14.14.1 中知道 for 语句的第一部分只执行一次,我们可以放心,迭代器方法不会被多次调用。

    【讨论】:

      【解决方案3】:

      Java LanguageSpecification14.14.2 说:

      EnhancedForStatement:
      for ( VariableModifiersopt Type Identifier: Expression) Statement

      如果 Expression 的类型是 Iterable 的子类型,则令 I 为表达式 Expression.iterator() 的类型。增强的 for 语句等价于基本的 for 语句,形式如下:

      for (I #i = Expression.iterator(); #i.hasNext(); ) {
      
              VariableModifiersopt Type Identifier = #i.next();
         Statement
      }
      

      #i 是编译器生成的 不同于任何标识符的标识符 其他标识符(编译器生成的 或其他)在范围内(§6.3) 在增强的点 语句发生。

      所以表达式只计算一次(在初始化期间)。

      【讨论】:

        【解决方案4】:

        getComponents 是否在每个 迭代?

        没有。 Java的foreach循环基于Iterable接口,相当于这段代码

        Iterator<Component> i = container.getComponents().iterator
        while(i.hasNext()) {
            Component c = i.next();
            // loop body goes here
        }
        

        唯一的区别是迭代器是隐藏的。

        【讨论】:

          【解决方案5】:

          我原以为在编译时,它会被翻译成一个“长手” for 循环,就像使用 for(int i = 0 ....

          难道 foreach 循环只是一个节省编程时间的程序,而实际的字节码翻译与“长期”执行它是一样的吗??

          简而言之,getComponent() 应该只被调用一次

          【讨论】:

          • 不,这不是 foreach 循环的工作方式(尽管 getComponent() 确实只会被调用一次)。
          • 这不值得-1(您给出的实际答案是正确的,尽管之前的假设是错误的),所以这里是+1。
          猜你喜欢
          • 2013-11-20
          • 2012-12-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多