【发布时间】:2009-10-09 13:22:18
【问题描述】:
我有这个 for-each-loop:
for (Component c : container.getComponents()) {
// Loop code
}
每次迭代都会调用getComponents 吗?在查看之前调用getComponents 并且只对缓存数组起作用是否有意义?
【问题讨论】:
标签: java performance loops
我有这个 for-each-loop:
for (Component c : container.getComponents()) {
// Loop code
}
每次迭代都会调用getComponents 吗?在查看之前调用getComponents 并且只对缓存数组起作用是否有意义?
【问题讨论】:
标签: java performance loops
不,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 }
【讨论】:
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 语句的第一部分只执行一次,我们可以放心,迭代器方法不会被多次调用。
【讨论】:
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) 在增强的点 语句发生。
所以表达式只计算一次(在初始化期间)。
【讨论】:
getComponents 是否在每个 迭代?
没有。 Java的foreach循环基于Iterable接口,相当于这段代码
Iterator<Component> i = container.getComponents().iterator
while(i.hasNext()) {
Component c = i.next();
// loop body goes here
}
唯一的区别是迭代器是隐藏的。
【讨论】:
我原以为在编译时,它会被翻译成一个“长手” for 循环,就像使用 for(int i = 0 ....
难道 foreach 循环只是一个节省编程时间的程序,而实际的字节码翻译与“长期”执行它是一样的吗??
简而言之,getComponent() 应该只被调用一次
【讨论】: