我一直想知道,在一般情况下,在循环之前声明一个丢弃的变量,而不是在循环内部重复,使任何(性能)差异? Java中的一个(非常无意义的)例子:

A)循环前声明:

double intermediateResult;
for(int i=0; i < 1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

B)声明(重复)内部循环:

for(int i=0; i < 1000; i++){
    double intermediateResult = i;
    System.out.println(intermediateResult);
}

哪个更好,a还是b?

我怀疑重复的变量声明(例b)在理论上会产生更多的开销,但编译器足够聪明,所以这无关紧要。例b的优点是更紧凑,并且将变量的作用域限制在使用它的地方。尽管如此,我还是倾向于根据示例a编写代码。

编辑:我对Java案例特别感兴趣。


当前回答

一位同事更喜欢第一种形式,说这是一种优化,更喜欢重用声明。

我更喜欢第二种(试着说服我的同事!);-)),已读到:

它将变量的范围缩小到需要的地方,这是一件好事。 Java进行了足够的优化,在性能上没有显著差异。IIRC,也许第二种形式更快。

无论如何,它属于依赖于编译器和/或JVM质量的过早优化的类别。

其他回答

这取决于语言和确切的用法。例如,在c# 1中,这没有什么区别。在c# 2中,如果局部变量是通过匿名方法(或c# 3中的lambda表达式)捕获的,则会产生非常显著的差异。

例子:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        int outer;
        for (int i=0; i < 10; i++)
        {
            outer = i;
            int inner = i;
            actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
        }

        foreach (Action action in actions)
        {
            action();
        }
    }
}

输出:

Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9

不同之处在于,所有的操作都捕获相同的外部变量,但每个操作都有自己独立的内部变量。

我怀疑一些编译器可以将两者优化为相同的代码,但肯定不是全部。所以我觉得你还是选择前者比较好。使用后者的唯一原因是希望确保声明的变量仅在循环中使用。

以下是我在。net中编写和编译的内容。

double r0;
for (int i = 0; i < 1000; i++) {
    r0 = i*i;
    Console.WriteLine(r0);
}

for (int j = 0; j < 1000; j++) {
    double r1 = j*j;
    Console.WriteLine(r1);
}

这是我从。net Reflector中得到的,当CIL被渲染回代码时。

for (int i = 0; i < 0x3e8; i++)
{
    double r0 = i * i;
    Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
    double r1 = j * j;
    Console.WriteLine(r1);
}

So both look exactly same after compilation. In managed languages code is converted into CL/byte code and at time of execution it's converted into machine language. So in machine language a double may not even be created on the stack. It may just be a register as code reflect that it is a temporary variable for WriteLine function. There are a whole set optimization rules just for loops. So the average guy shouldn't be worried about it, especially in managed languages. There are cases when you can optimize manage code, for example, if you have to concatenate a large number of strings using just string a; a+=anotherstring[i] vs using StringBuilder. There is very big difference in performance between both. There are a lot of such cases where the compiler cannot optimize your code, because it cannot figure out what is intended in a bigger scope. But it can pretty much optimize basic things for you.

哪个更好,a还是b?

从性能的角度来看,你必须衡量它。(在我看来,如果你能测量到差异,编译器不是很好)。

从维护的角度来看,b更好。在同一个地方声明和初始化变量,在尽可能窄的范围内。不要在声明和初始化之间留下空隙,不要污染不需要的名称空间。

我总是使用A(而不是依赖于编译器),也可能重写为:

for(int i=0, double intermediateResult=0; i<1000; i++){
    intermediateResult = i;
    System.out.println(intermediateResult);
}

这仍然将intermediateResult限制在循环的范围内,但不会在每次迭代期间重新声明。