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

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

例如,如果我有:

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

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

关于增强for循环的语义

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

JLS 14.14.2增强的for语句

增强的for语句具有以下forms:

 for ( Type Identifier : Expression ) Statement 

如果Expression的类型是数组类型T[] ,那么增强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增强的for语句

增强的for语句具有以下forms:

 for ( Type Identifier : Expression ) Statement 

如果Expression的类型是Iterable的子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只评估一次), 正是因为这是许多场景中的首选构造 ,编译器将确保尽可能地优化它们。

也可以看看

  • Java语言指南/ for-each循环
    • 有惯用法的例子以及它如何帮助减少错误的机会

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

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

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

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

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

编辑 – 答案随收到的评论而改变。

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

例:

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