【发布时间】:2010-06-04 12:13:52
【问题描述】:
如果 javac 做我认为的那样,以下几行将产生相同的性能:
for (Object o: getObjects()) {}
List<Object> os = getObjects(); for (Object o: os) {}
是这样还是不是?或者它可能是特定于实现的?如果是这样:有人知道 GWT 吗?
【问题讨论】:
如果 javac 做我认为的那样,以下几行将产生相同的性能:
for (Object o: getObjects()) {}
List<Object> os = getObjects(); for (Object o: os) {}
是这样还是不是?或者它可能是特定于实现的?如果是这样:有人知道 GWT 吗?
【问题讨论】:
性能将相同。
Java 编译器通过在您循环的对象上调用 iterator() 方法,将 for-each 循环变成围绕 Iterator 对象的循环。
因此,实际的列表实例只使用一次。 (拨打iterator())
【讨论】:
来自 Java 语言规范:
14.14.2 The enhanced for statement
增强的 for 语句具有 形式:
EnhancedForStatement: for ( VariableModifiersopt Type Identifier: Expression) Statement表达式必须具有类型
Iterable否则它必须是 数组类型(§10.1),或编译时 发生错误。声明的局部变量的范围 在 FormalParameter 部分 增强的
for语句(§14.14)是 包含的声明增强
for的含义 声明是通过翻译成 一个基本的for声明。如果
Expression的类型是Iterable的子类型,然后让I为 表达式的类型 表达式。iterator()。增强的for语句是等价的 到基本的for声明 形式:for (I #i = Expression.iterator(); #i.hasNext(); ) { VariableModifiersopt Type Identifier = #i.next(); Statement }其中
#i是编译器生成的 不同于任何标识符的标识符 其他标识符(编译器生成的 或其他)在范围内(§6.3) 在增强的点 语句发生。
如您所见,Expression 仅在 for 循环表达式的第一部分中被提及,因此只计算一次。所以你的两条线会产生相同的性能。
【讨论】:
从纯 Java 的角度来看,这些答案似乎都是正确的。此外,如果可以的话,GWT 编译器实际上会在生成 JavaScript 之前将增强的 for 循环进一步重写为常规 for 循环。所以它实际上最终会看起来像:
for (int i = 0; i < getObjects().size(); i++) {
Object o = getObjects().get(i);
// ...
}
原因?如果 List 迭代器对象从未被引用,它可以被声明为死代码并且不会用 JavaScript 重写,从而导致下载大小更小。这种优化应该对代码的实际执行没有任何影响。
请参阅今年的 Google I/O 中的 Optimizing apps with the GWT compiler,了解有关 GWT 编译器为减小 JS 大小所做的其他疯狂事情的更多详细信息。
【讨论】:
从实用的角度来看,您可以检查两个字节码并进行比较:
m1()V
L0
ALOAD 0
INVOKEVIRTUAL it/funge/Console.getObjects()Ljava/util/List;
INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
ASTORE 2
GOTO L1
L2
FRAME FULL [it/funge/Console T java/util/Iterator] []
ALOAD 2
INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
ASTORE 1
L1
FRAME SAME
ALOAD 2
INVOKEINTERFACE java/util/Iterator.hasNext()Z
IFNE L2
L3
RETURN
m2()V
L0
ALOAD 0
INVOKEVIRTUAL it/funge/Console.getObjects()Ljava/util/List;
ASTORE 1
L1
ALOAD 1
INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;
ASTORE 3
GOTO L2
L3
FRAME FULL [it/funge/Console java/util/List T java/util/Iterator] []
ALOAD 3
INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object;
ASTORE 2
L2
FRAME SAME
ALOAD 3
INVOKEINTERFACE java/util/Iterator.hasNext()Z
IFNE L3
L4
RETURN
它们是相等的,唯一的区别是你的第二个 sn-p 有两个单独的部分来加载 List 然后获取迭代器。这也会消耗更多的类本地人,事实上它还有一个ALOAD和一个ASTORE更多,它用于通过两行代码存储getObjects结果,而在你的第一个sn-p中它直接使用它..
【讨论】:
没有区别。 foreach 构造只是从对象中获取一个Iterator。这就是为什么它必须实现Iterable 接口。
【讨论】: