7

如果 javac 做我认为的,以下几行将产生相同的性能:

for (Object o: getObjects()) {}
List<Object> os = getObjects(); for (Object o: os) {}

是这样还是不是?或者它可能是特定于实现的?如果是这样:有人知道 GWT 吗?

4

5 回答 5

8

来自 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 循环表达式的第一部分中提到,因此只计算一次。所以你的两条线会产生相同的性能。

于 2010-06-04T12:21:49.390 回答
8

性能将是相同的。

IteratorJava 编译器通过调用您循环的对象上的方法,将 for-each 循环变成围绕对象的iterator()循环。
因此,实际的列表实例只使用一次。(打电话iterator()

于 2010-06-04T12:17:15.780 回答
5

从纯 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 编译器优化应用程序。

于 2010-06-04T14:06:39.177 回答
4

从实际的角度来看,您可以检查两个字节码并进行比较:

 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通过两行代码存储结果,而在你的第一个片段中它直接使用它..

于 2010-06-04T12:29:01.857 回答
2

没有区别。Iteratorforeach 构造只是从对象中获取一个。这就是为什么它必须实现Iterable接口。

于 2010-06-04T12:18:23.040 回答