如果 javac 做我认为的,以下几行将产生相同的性能:
for (Object o: getObjects()) {}
List<Object> os = getObjects(); for (Object o: os) {}
是这样还是不是?或者它可能是特定于实现的?如果是这样:有人知道 GWT 吗?
来自 Java 语言规范:
14.14.2 增强的 for 语句
增强的 for 语句具有以下形式:
EnhancedForStatement: for ( VariableModifiersopt Type Identifier: Expression) Statement
表达式必须具有类型
Iterable
,否则它必须是数组类型(第 10.1 节),否则会发生编译时错误。在增强
for
语句(第 14.14 节)的 FormalParameter 部分中声明的局部变量的范围是包含的语句增强语句的含义
for
通过翻译成基本for
语句来给出。如果 的 类型
Expression
是 的子类型Iterable
,则令I
为表达式 Expression 的类型。iterator()
. 增强for
语句等价于for
以下形式的基本语句:for (I #i = Expression.iterator(); #i.hasNext(); ) { VariableModifiersopt Type Identifier = #i.next(); Statement }
where
#i
是一个编译器生成的标识符,它与增强 for 语句发生时范围内(第 6.3 节)内的任何其他标识符(编译器生成的或其他标识符)不同。
如您所见,Expression
仅在 for 循环表达式的第一部分中提到,因此只计算一次。所以你的两条线会产生相同的性能。
性能将是相同的。
Iterator
Java 编译器通过调用您循环的对象上的方法,将 for-each 循环变成围绕对象的iterator()
循环。
因此,实际的列表实例只使用一次。(打电话iterator()
)
从纯 Java 的角度来看,这些答案似乎都是正确的。此外,如果可以,GWT 编译器实际上会在生成 JavaScript 之前将增强的 for 循环进一步重写为常规 for 循环。所以它实际上最终会看起来像:
for (int i = 0; i < getObjects().size(); i++) {
Object o = getObjects().get(i);
// ...
}
原因?如果 List 迭代器对象从未被引用,它可以被声明为死代码并且不会用 JavaScript 重写,从而导致下载量更小。这种优化应该对代码的实际执行没有任何影响。
有关 GWT 编译器为减少 JS 大小所做的其他疯狂事情的更多详细信息,请参阅今年的 Google I/O 中的使用 GWT 编译器优化应用程序。
从实际的角度来看,您可以检查两个字节码并进行比较:
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
它们是相等的,唯一的区别是您的第二个片段有两个单独的部分来加载List
然后获取迭代器。这也将消耗更多的类的本地人,事实上它还有一个ALOAD
和一个ASTORE
更多,它用于getObjects
通过两行代码存储结果,而在你的第一个片段中它直接使用它..
没有区别。Iterator
foreach 构造只是从对象中获取一个。这就是为什么它必须实现Iterable
接口。