【问题标题】:Will the "count limit" expression of a for-loop be evaluated only once, or on each iteration?for 循环的“计数限制”表达式是否只计算一次,还是在每次迭代时计算?
【发布时间】:2011-11-08 15:14:36
【问题描述】:

如果我在循环的条件语句中调用一个方法,是否会在每次循环迭代时调用它?

例如:

for( int i = 0; i <= expensiveComputation(); i++ ) {
    // Do something.
}

我会在每次迭代中执行expensiveComputation() 吗?或者expensiveComputation()的结果会在循环变量初始化的同时被存储并在每次迭代中使用?

我应该改写成这样吗:

int max = expensiveComputation();
for ( int i = 0; i <= max; i++ ) {
    // Do something.
}

【问题讨论】:

    标签: java loops methods for-loop call


    【解决方案1】:

    它将在每次迭代时被调用,除非编译器/优化器确定它没有副作用并且可能将调用作为优化消除。

    我的意思是,编译器不能只是盲目地存储值,因为 java 中的函数与数学函数不同,它不仅可以具有返回值,还可以具有诸如将某些内容打印到某些流或更改某些全局的副作用状态等

    编译器不能省略每次迭代的调用还有另一个原因。您的函数不接受任何参数这一事实并不意味着它每次都必然返回相同的值。例如,它可以从流中输入一个数字并返回它,也可以随机生成一个数字。

    所以编译器需要非常小心才能安全地消除调用。所以,如果函数很昂贵,你一定要预先存储它的值。

    【讨论】:

    • 谢谢,我从来没有这么想过! ;)
    • 按照同样的思路思考,你会说:for( Object obj : getAllObjects() ) {} 会在每次迭代中重新抓取一个新集合吗?
    • @BARNZ:嗯.. 那不一样。 foreach 循环确实不同。我想在 foreach 循环的情况下,它不会在每次迭代时都这样做。但我不确定
    • 增强的 for 语句的“脱糖”版本在 JLS 中给出,并表明它确实在每次迭代中评估表达式:Expression (在这种情况下,getAllObjects()) 仅出现在等效基本 for 循环的 ForInit 部分中,该循环仅被评估一次。
    【解决方案2】:

    第二个选项更好,特别是如果计算不需要在每次迭代时计算


    如果你假设你的循环是 n 长度并且你的计算是 O(n)。
    第一种方案的复杂度为 O(n2) 而另一个为 O(2n)

    【讨论】:

      【解决方案3】:

      它可能会在每次迭代时被调用,也可能不会被调用,这取决于编译器感觉要发出什么字节码。实际上很可能每次都会调用它。

      如果您想确保不会在每次迭代中调用它,请使用int max = ... 版本。

      为什么不简单地自己测试一下?

      【讨论】:

      • 其实这里的Java编译器没有那么多选择。如果发生任何优化,则由 Hotspot JIT 编译器在运行时进行。
      【解决方案4】:

      是的,你应该在这些情况下缓存你的结果,以确保更好的性能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-27
        • 2011-03-09
        • 1970-01-01
        • 1970-01-01
        • 2013-10-11
        • 2016-05-20
        • 1970-01-01
        相关资源
        最近更新 更多