我一直想知道,在一般情况下,在循环之前声明一个丢弃的变量,而不是在循环内部重复,使任何(性能)差异? 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案例特别感兴趣。


当前回答

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

其他回答

以下是我在。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.

好吧,你可以为它设定一个范围:

{ //Or if(true) if the language doesn't support making scopes like this
    double intermediateResult;
    for (int i=0; i<1000; i++) {
        intermediateResult = i;
        System.out.println(intermediateResult);
    }
}

这样你只声明了变量一次,当你离开循环时它就死了。

如果你在lambda中使用变量,在c#中是有区别的。但一般来说,编译器基本上会做同样的事情,假设变量只在循环中使用。

鉴于它们基本上是相同的:请注意,版本b对读者来说更明显的是,变量不是,也不能在循环之后使用。此外,版本b更容易重构。在版本a中,将循环体提取到它自己的方法中更加困难。而且,版本b向您保证这样的重构没有副作用。

因此,版本a让我很恼火,因为它没有任何好处,而且它让我更难理解代码……

作为一般规则,我将变量声明在最内部的可能范围内。如果你不在循环外使用intermediateResult,那么我会用B。

从性能的角度来看,外部(要好得多)。

public static void outside() {
    double intermediateResult;
    for(int i=0; i < Integer.MAX_VALUE; i++){
        intermediateResult = i;
    }
}

public static void inside() {
    for(int i=0; i < Integer.MAX_VALUE; i++){
        double intermediateResult = i;
    }
}

两个函数我都执行了10亿次。 Outside()耗时65毫秒。Inside()耗时1.5秒。