Java是如何由编译器生成的循环代码

时间:2010-08-08 06:08:47

标签: java foreach

编译器如何生成Java的循环代码?

例如,如果我有:

for(String s : getStringArray() )
{
   //do something with s
}

其中getStringArray()是一个返回我想要循环的数组的函数,函数会一直调用还是只调用一次?一般来说,使用这种结构循环的代码有多优化?

4 个答案:

答案 0 :(得分:52)

关于增强型for循环

的语义

以下是 Java语言规范第3版的相关摘录,为了清晰起见,略有编辑:

  

JLS 14.14.2 The enhanced for statement

     

增强型for语句的格式为:

for ( Type Identifier : Expression ) Statement
     

如果Expression的类型是数组类型T[],则增强的for语句的含义由以下基本for语句给出:

T[] a = Expression;
for (int i = 0; i < a.length; i++) {
    Type Identifier = a[i];
    Statement
}
     

其中ai是编译器生成的标识符,它们与增强型for语句的范围内的任何其他标识符(编译器生成的或其他标识符)不同发生。

事实上,该语言确保 Expression 只会被评估一次。

为了完整性,Expression类型为Iterable时,这是等效性:

  

JLS 14.14.2 The enhanced for statement

     

增强型for语句的格式为:

for ( Type Identifier : Expression ) Statement
     

如果Expression的类型是Iterable的子类型,则让I成为表达式Expression.iterator()的类型。增强的for语句相当于表单的基本for语句:

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

其中iter是编译器生成的标识符,它与发生增强for语句的范围内的任何其他标识符(编译器生成的或其他标识符)不同。

请注意,如果Expression既不是Iterable也不是数组,那么这是一个编译时错误,所以上面两个是唯一可以使用增强for循环的情况。另外,为清楚起见,上述引号中没有关于for循环上附加的任何标签以及Identifier上附加的任何修饰符的信息,但这些都是按照预期处理的。


关于增强型for循环

的性能

以下是 Effective Java 2nd Edition的引用,第46项:首选每个循环到传统for循环

  

在1.5版中引入的for-each循环通过完全隐藏迭代器或索引变量来消除混乱和错误的机会。由此产生的习语同样适用于集合和数组。 请注意,即使对于数组,使用for-each循环也不会有性能损失。事实上,在某些情况下,它可能比普通的for循环提供轻微的性能优势,因为它只计算一次数组索引的限制。虽然可以手动执行此操作,但程序员并不总是这样做。

因此,本书声称实际上有些编译器超越了JLS转换并对for-each循环执行了额外的优化(当然,仍然保持其语义)。

总之,您不必担心for-each循环的性能。该语言的规范是合理的(Expression仅评估一次),而正是因为这是许多场景中的首选构造,编译器将确保尽可能地优化它们。< / p>

另见

答案 1 :(得分:1)

JDK 1.4引入了RandomAcces接口。它旨在给出算法的提示,对于给定的List实现,迭代更有效:

for (int i=0, n=list.size(); i &lt; n; i++) {
          list.get(i);
}

大于

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

foreach循环是否考虑到了这一点?或者它是否完全忽略了给定的Iterable实际上是List?的事实。应该注意的是,这意味着要向List添加(iterable instanceof List && iterable instanceof RandomAccess)测试和向下转换,这将增加一个并不总是值得的开销,并且可以被认为是编译器语法糖的预先优化 feature。

答案 2 :(得分:-1)

编译器可能只调用一次,但可以依赖它。它可能不是一个好的编码实践。 如果getStringArray()每次返回相同的数组,为什么不首先设置变量?

编辑 - 根据收到的评论更改了答案。

答案 3 :(得分:-6)

for循环与javascript中的循环相同,所以不必害怕

示例:

for(int i=0;i<10;i++)
{
    System.out.Println(i);
}