【问题标题】:Java for-each on getterJava for-each on getter
【发布时间】:2010-06-04 12:13:52
【问题描述】:

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

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

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

【问题讨论】:

    标签: java gwt foreach getter


    【解决方案1】:

    性能将相同。

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        从纯 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 大小所做的其他疯狂事情的​​更多详细信息。

        【讨论】:

          【解决方案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
          

          它们是相等的,唯一的区别是你的第二个 sn-p 有两个单独的部分来加载 List 然后获取迭代器。这也会消耗更多的类本地人,事实上它还有一个ALOAD和一个ASTORE更多,它用于通过两行代码存储getObjects结果,而在你的第一个sn-p中它直接使用它..

          【讨论】:

            【解决方案5】:

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

            【讨论】:

              猜你喜欢
              • 2015-10-27
              • 2011-07-21
              • 1970-01-01
              • 2012-10-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-04-30
              • 2018-07-19
              相关资源
              最近更新 更多