¿Diferencia entre declarar variables antes o en bucle?

312

Siempre me he preguntado si, en general, declarar una variable desechable antes de un ciclo, en lugar de repetidamente dentro del ciclo, ¿hace alguna diferencia (de rendimiento)? Un ejemplo (bastante inútil) en Java:

a) declaración antes del bucle:

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

b) declaración (repetidamente) dentro del bucle:

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

¿Cuál es mejor, una o b ?

Sospecho que la declaración variable repetida (ejemplo b ) crea más sobrecarga en teoría , pero que los compiladores son lo suficientemente inteligentes como para que no importe. El ejemplo b tiene la ventaja de ser más compacto y limitar el alcance de la variable al lugar donde se usa. Aún así, tiendo a codificar según el ejemplo a .

Editar: Estoy especialmente interesado en el caso de Java.

Rabarberski
fuente
Esto es importante al escribir código Java para la plataforma Android. Google sugiere que para que el código de tiempo crítico declare variables incrementales fuera de un ciclo for, como si estuviera dentro del ciclo for, lo vuelve a declarar cada vez en ese entorno. La diferencia de rendimiento es muy notable para los algoritmos caros.
AaronCarson
1
@AaronCarson, ¿podría proporcionar un enlace a esta sugerencia de Google?
Vitaly Zinchenko

Respuestas:

256

¿Cuál es mejor, a o b ?

Desde una perspectiva de rendimiento, tendrías que medirlo. (Y en mi opinión, si puedes medir la diferencia, el compilador no es muy bueno).

Desde una perspectiva de mantenimiento, b es mejor. Declare e inicialice las variables en el mismo lugar, en el ámbito más estrecho posible. No deje un hueco entre la declaración y la inicialización, y no contamine los espacios de nombres que no necesita.

Daniel Earwicker
fuente
55
En lugar de Double, si se trata de String, ¿es mejor el caso "b"?
Antoops
3
@Antoops: sí, b es mejor por razones que no tienen nada que ver con el tipo de datos de la variable que se declara. ¿Por qué sería diferente para Strings?
Daniel Earwicker
215

Bueno, ejecuté sus ejemplos A y B 20 veces cada uno, haciendo un bucle de 100 millones de veces. (JVM - 1.5.0)

A: tiempo de ejecución promedio: 0,074 segundos

B: tiempo de ejecución promedio: 0,067 segundos

Para mi sorpresa, B fue un poco más rápido. Tan rápido como las computadoras son ahora es difícil decir si podría medir esto con precisión. También lo codificaría como A pero diría que realmente no importa.

marca
fuente
12
Me venciste. Estaba a punto de publicar mis resultados para el perfil, obtuve más o menos lo mismo y sí, sorprendentemente, B es más rápido, realmente habría pensado A si hubiera apostado por él.
Mark Davidson el
14
No es de extrañar: cuando la variable es local para el bucle, no necesita conservarse después de cada iteración, por lo que puede permanecer en un registro.
142
+1 para probarlo realmente , no solo una opinión / teoría que el OP podría haber inventado él mismo.
MGOwen 05 de
3
@GoodPerson para ser honesto, me gustaría que se hiciera eso. Ejecuté esta prueba alrededor de 10 veces en mi máquina durante 50,000,000-100,000,000 de iteraciones con un código casi idéntico (que me encantaría compartir con cualquiera que quiera ejecutar estadísticas). Las respuestas se dividieron casi por igual en ambos sentidos, generalmente por un margen de 900 ms (más de 50 millones de iteraciones), que en realidad no es mucho. Aunque mi primer pensamiento es que va a ser "ruido", podría inclinarse un poco. Sin embargo, este esfuerzo me parece puramente académico (para la mayoría de las aplicaciones de la vida real). Me encantaría ver un resultado de todos modos;) ¿Alguien está de acuerdo?
javatarz
3
Mostrar resultados de la prueba sin documentar la configuración no tiene ningún valor. Eso es especialmente cierto en este caso, donde ambos fragmentos de código producen un código de bytes idéntico, por lo que cualquier diferencia medida es solo un signo de condiciones de prueba insuficientes.
Holger
66

Depende del idioma y el uso exacto. Por ejemplo, en C # 1 no hizo ninguna diferencia. En C # 2, si la variable local es capturada por un método anónimo (o expresión lambda en C # 3), puede hacer una diferencia muy significativa.

Ejemplo:

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();
        }
    }
}

Salida:

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

La diferencia es que todas las acciones capturan la misma outervariable, pero cada una tiene su propia innervariable separada .

Jon Skeet
fuente
3
en el ejemplo B (pregunta original), ¿crea realmente una nueva variable cada vez? ¿Qué pasa a los ojos de la pila?
Royi Namir
@ Jon, ¿fue un error en C # 1.0? ¿No debería ser idealmente Outer9?
nawfal
@nawfal: No sé a qué te refieres. Las expresiones lambda no estaban en 1.0 ... y Outer es 9. ¿Qué error quieres decir?
Jon Skeet
@nawfal: Mi punto es que no había ninguna función de lenguaje en C # 1.0 donde se notara la diferencia entre declarar una variable dentro de un bucle y declararla afuera (suponiendo que ambas compiladas). Eso cambió en C # 2.0. No hay error
Jon Skeet
@ JonSkeet Oh, sí, te entiendo ahora, pasé por alto el hecho de que no puedes cerrar variables como esa en 1.0, ¡qué mal! :)
nawfal
35

Lo siguiente es lo que escribí y compilé en .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);
}

Esto es lo que obtengo de .NET Reflector cuando CIL se vuelve a representar en el código.

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);
}

Entonces ambos se ven exactamente iguales después de la compilación. En los idiomas administrados, el código se convierte en código CL / byte y en el momento de la ejecución se convierte en lenguaje máquina. Entonces, en lenguaje de máquina, es posible que ni siquiera se cree un doble en la pila. Puede ser solo un registro ya que el código refleja que es una variable temporal para la WriteLinefunción. Hay un conjunto completo de reglas de optimización solo para bucles. Por lo tanto, el chico promedio no debería preocuparse por eso, especialmente en los idiomas administrados. Hay casos en los que puede optimizar el código de administración, por ejemplo, si tiene que concatenar una gran cantidad de cadenas usando solo string a; a+=anotherstring[i]vs usandoStringBuilder. Hay una gran diferencia en el rendimiento entre ambos. Hay muchos casos en los que el compilador no puede optimizar su código, porque no puede averiguar qué se pretende en un ámbito mayor. Pero puede optimizar las cosas básicas para usted.

partícula
fuente
int j = 0 para (; j <0x3e8; j ++) de esta manera declarada una vez por vez ambas variables, y no cada una por ciclo. 2) la tarea es más grasa que cualquier otra opción. 3) Entonces, la regla de la mejor práctica es cualquier declaración fuera de la iteración para.
luka
24

Este es un problema en VB.NET. El resultado de Visual Basic no reiniciará la variable en este ejemplo:

For i as Integer = 1 to 100
    Dim j as Integer
    Console.WriteLine(j)
    j = i
Next

' Output: 0 1 2 3 4...

Esto imprimirá 0 la primera vez (¡las variables de Visual Basic tienen valores predeterminados cuando se declaran!) Pero icada vez después de eso.

Sin embargo, si agrega un = 0, obtendrá lo que podría esperar:

For i as Integer = 1 to 100
    Dim j as Integer = 0
    Console.WriteLine(j)
    j = i
Next

'Output: 0 0 0 0 0...
Michael Haren
fuente
1
¡He estado usando VB.NET durante años y no había encontrado esto!
ChrisA
12
Sí, es desagradable resolver esto en la práctica.
Michael Haren el
Aquí hay una referencia sobre esto de Paul Vick: panopticoncentral.net/archive/2006/03/28/11552.aspx
ferventcoder
1
@eschneider @ferventcoder Lamentablemente, @PaulV ha decidido abandonar sus antiguas publicaciones de blog , por lo que ahora es un enlace inactivo .
Mark Hurd
sí, recientemente me encontré con esto; estaba buscando algunos documentos oficiales sobre esto ...
Eric Schneider
15

Hice una prueba simple:

int b;
for (int i = 0; i < 10; i++) {
    b = i;
}

vs

for (int i = 0; i < 10; i++) {
    int b = i;
}

Compilé estos códigos con gcc - 5.2.0. Y luego desarme el main () de estos dos códigos y ese es el resultado:

1º:

   0x00000000004004b6 <+0>:     push   rbp
   0x00000000004004b7 <+1>:     mov    rbp,rsp
   0x00000000004004ba <+4>:     mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret

vs

   0x00000000004004b6 <+0>: push   rbp
   0x00000000004004b7 <+1>: mov    rbp,rsp
   0x00000000004004ba <+4>: mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004004c1 <+11>:    jmp    0x4004cd <main+23>
   0x00000000004004c3 <+13>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004004c6 <+16>:    mov    DWORD PTR [rbp-0x8],eax
   0x00000000004004c9 <+19>:    add    DWORD PTR [rbp-0x4],0x1
   0x00000000004004cd <+23>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x00000000004004d1 <+27>:    jle    0x4004c3 <main+13>
   0x00000000004004d3 <+29>:    mov    eax,0x0
   0x00000000004004d8 <+34>:    pop    rbp
   0x00000000004004d9 <+35>:    ret 

Que son exactamente el mismo resultado. ¿No es una prueba de que los dos códigos producen la misma cosa?

UsuarioX
fuente
3
sí, y es genial que hayas hecho esto, pero esto vuelve a lo que la gente decía sobre la dependencia del lenguaje / compilador. Me pregunto cómo se vería afectado el rendimiento del lenguaje JIT o interpretado.
user137717
12

Depende del lenguaje: IIRC C # optimiza esto, por lo que no hay ninguna diferencia, pero JavaScript (por ejemplo) hará todo el shebang de asignación de memoria cada vez.

annakata
fuente
Sí, pero eso no equivale a mucho. Ejecuté una prueba simple con un bucle for ejecutándose 100 millones de veces y descubrí que la mayor diferencia a favor de declarar fuera del bucle era de 8 ms. Por lo general, se parecía más a 3-4 y, ocasionalmente, declarar fuera del ciclo funcionaba PEOR (hasta 4 ms), pero eso no era típico.
user137717
11

Siempre usaría A (en lugar de depender del compilador) y también podría volver a escribir en:

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

Esto todavía se restringe intermediateResultal alcance del bucle, pero no se redeclara durante cada iteración.

Tríptico
fuente
12
¿Desea conceptualmente que la variable viva durante la duración del ciclo en lugar de por separado por iteración? Raramente lo hago. Escriba un código que revele su intención lo más claramente posible, a menos que tenga una muy, muy buena razón para hacerlo de otra manera.
Jon Skeet el
44
Ah, buen compromiso, ¡nunca pensé en esto! OMI, el código se vuelve un poco menos visualmente 'claro')
Rabarberski
2
@ Jon - No tengo idea de lo que el OP está haciendo realmente con el valor intermedio. Solo pensé que era una opción que valía la pena considerar.
Tríptico el
6

En mi opinión, b es la mejor estructura. En a, el último valor de intermediarioResultado se queda después de que finaliza su ciclo.

Editar: Esto no hace mucha diferencia con los tipos de valor, pero los tipos de referencia pueden ser algo pesados. Personalmente, me gusta que las variables sean desreferenciadas lo antes posible para la limpieza, y b lo hace por usted,

Powerlord
fuente
sticks around after your loop is finished- aunque esto no importa en un lenguaje como Python, donde los nombres enlazados permanecen hasta que finaliza la función.
nuevo123456
@ new123456: El OP solicitó detalles de Java, incluso si la pregunta se hizo de forma algo genérica. Muchos lenguajes derivados de C tienen un alcance de nivel de bloque: C, C ++, Perl (con la mypalabra clave), C # y Java para nombrar 5 que he usado.
Powerlord
Lo sé, fue una observación, no una crítica.
nuevo123456
5

Sospecho que algunos compiladores podrían optimizar ambos para ser el mismo código, pero ciertamente no todos. Entonces diría que estás mejor con lo primero. La única razón para esto último es si desea asegurarse de que la variable declarada se use solo dentro de su ciclo.

Estofado S
fuente
5

Como regla general, declaro mis variables en el ámbito más interno posible. Por lo tanto, si no está utilizando el resultado intermedio fuera del bucle, entonces iría con B.

Chris
fuente
5

Un compañero de trabajo prefiere la primera forma, diciéndole que es una optimización, prefiriendo reutilizar una declaración.

Prefiero el segundo (¡y trato de persuadir a mi compañero de trabajo! ;-)), después de leer eso:

  • Reduce el alcance de las variables a donde se necesitan, lo cual es algo bueno.
  • Java se optimiza lo suficiente como para no hacer una diferencia significativa en el rendimiento. IIRC, quizás la segunda forma es aún más rápida.

De todos modos, cae en la categoría de optimización prematura que se basa en la calidad del compilador y / o JVM.

PhiLho
fuente
5

Hay una diferencia en C # si está utilizando la variable en un lambda, etc. Pero, en general, el compilador básicamente hará lo mismo, suponiendo que la variable solo se use dentro del ciclo.

Dado que son básicamente lo mismo: tenga en cuenta que la versión b hace que sea mucho más obvio para los lectores que la variable no se puede usar, y no se puede, después del ciclo. Además, la versión b se refactoriza mucho más fácilmente. Es más difícil extraer el cuerpo del bucle en su propio método en la versión a. Además, la versión b le asegura que no existe ningún efecto secundario en dicha refactorización.

Por lo tanto, la versión a me molesta sin fin, porque no tiene ningún beneficio y hace que sea mucho más difícil razonar sobre el código ...

Mark Sowul
fuente
5

Bueno, siempre puedes hacer un alcance para eso:

{ //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);
    }
}

De esta manera, solo declaras la variable una vez, y morirá cuando abandones el ciclo.

Marcelo Faísca
fuente
4

Siempre he pensado que si declaras tus variables dentro de tu ciclo, estás desperdiciando memoria. Si tienes algo como esto:

for(;;) {
  Object o = new Object();
}

Entonces, no solo es necesario crear el objeto para cada iteración, sino que debe haber una nueva referencia asignada para cada objeto. Parece que si el recolector de basura es lento, entonces tendrá un montón de referencias colgantes que deben limpiarse.

Sin embargo, si tienes esto:

Object o;
for(;;) {
  o = new Object();
}

Entonces solo está creando una referencia única y asignándole un nuevo objeto cada vez. Claro, puede tomar un poco más de tiempo para que se salga del alcance, pero solo hay una referencia pendiente para tratar.

R. Carr
fuente
3
No se asigna una nueva referencia para cada objeto, incluso si la referencia se declara dentro del ciclo 'for'. En AMBOS casos: 1) 'o' es una variable local y el espacio de pila se asigna una vez al comienzo de la función. 2) Hay un nuevo Objeto creado en cada iteración. Por lo tanto, no hay diferencia en el rendimiento. Para la organización del código, la legibilidad y la facilidad de mantenimiento, es mejor declarar la referencia dentro del ciclo.
Ajoy Bhatia
1
Si bien no puedo hablar por Java, en .NET la referencia no está 'asignada' para cada objeto en el primer ejemplo. Hay una sola entrada en la pila para esa variable local (al método). Para sus ejemplos, la IL creada es idéntica.
Jesse C. Slicer
3

Creo que depende del compilador y es difícil dar una respuesta general.

SquidScareMe
fuente
3

Mi práctica es la siguiente:

  • si el tipo de variable es simple (int, double, ...) Prefiero la variante b (dentro).
    Motivo: reducción del alcance de la variable.

  • si el tipo de variable no es simple (algún tipo de classo struct) prefiero la variante a (fuera).
    Motivo: reducción del número de llamadas ctor-dtor.

grasa
fuente
1

Desde una perspectiva de rendimiento, el exterior es (mucho) mejor.

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;
    }
}

Ejecuté ambas funciones mil millones de veces cada una. outside () tomó 65 milisegundos. inside () tardó 1.5 segundos.

Alex
fuente
2
Debe haber depurado la compilación no optimizada entonces, ¿eh?
Tomasz Przychodzki
int j = 0 para (; j <0x3e8; j ++) de esta manera declarada una vez por vez ambas variables, y no cada una por ciclo. 2) la tarea es más grasa que cualquier otra opción. 3) Entonces, la regla de la mejor práctica es cualquier declaración fuera de la iteración para.
luka
1

Probé para JS con el Nodo 4.0.0 si alguien está interesado. La declaración fuera del ciclo resultó en una mejora de rendimiento de ~ .5 ms en promedio sobre 1000 pruebas con 100 millones de iteraciones de ciclo por prueba. Así que voy a decir adelante y escribirlo de la manera más legible / mantenible que es B, en mi opinión. Pondría mi código en un violín, pero usé el módulo Node de rendimiento ahora. Aquí está el código:

var now = require("../node_modules/performance-now")

// declare vars inside loop
function varInside(){
    for(var i = 0; i < 100000000; i++){
        var temp = i;
        var temp2 = i + 1;
        var temp3 = i + 2;
    }
}

// declare vars outside loop
function varOutside(){
    var temp;
    var temp2;
    var temp3;
    for(var i = 0; i < 100000000; i++){
        temp = i
        temp2 = i + 1
        temp3 = i + 2
    }
}

// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;

// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varInside()
    var end = now()
    insideAvg = (insideAvg + (end-start)) / 2
}

// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
    var start = now()
    varOutside()
    var end = now()
    outsideAvg = (outsideAvg + (end-start)) / 2
}

console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)
usuario137717
fuente
0

A) es una apuesta segura que B) ......... Imagínese si está inicializando la estructura en bucle en lugar de 'int' o 'float', ¿entonces qué?

me gusta

typedef struct loop_example{

JXTZ hi; // where JXTZ could be another type...say closed source lib 
         // you include in Makefile

}loop_example_struct;

//then....

int j = 0; // declare here or face c99 error if in loop - depends on compiler setting

for ( ;j++; )
{
   loop_example loop_object; // guess the result in memory heap?
}

¡Sin duda tendrá problemas de pérdida de memoria! Por lo tanto, creo que 'A' es una apuesta más segura, mientras que 'B' es vulnerable a la acumulación de memoria, especialmente al trabajar con bibliotecas de código cerrado.

Entusiasta
fuente
0

Es una pregunta interesante. Según mi experiencia, hay una pregunta fundamental a tener en cuenta al debatir este asunto para obtener un código:

¿Hay alguna razón por la cual la variable deba ser global?

Tiene sentido declarar la variable solo una vez, globalmente, en lugar de muchas veces localmente, porque es mejor para organizar el código y requiere menos líneas de código. Sin embargo, si solo necesita declararse localmente dentro de un método, lo inicializaría en ese método para que quede claro que la variable es exclusivamente relevante para ese método. Tenga cuidado de no llamar a esta variable fuera del método en el que se inicializa si elige la última opción: su código no sabrá de qué está hablando e informará un error.

Además, como nota al margen, no duplique nombres de variables locales entre diferentes métodos, incluso si sus propósitos son casi idénticos; solo se vuelve confuso.

Joshua Siktar
fuente
1
lol no estoy de acuerdo por muchas razones ... Sin embargo, no vote down ... respeto tu derecho a elegir
Grantly
0

esta es la mejor forma

double intermediateResult;
int i = byte.MinValue;

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

1) de esta manera se declara una vez ambas variables, y no cada una por ciclo. 2) la tarea es más grasa que cualquier otra opción. 3) Entonces, la regla de la mejor práctica es cualquier declaración fuera de la iteración para.

luka
fuente
0

Probé lo mismo en Go, y comparé la salida del compilador go tool compile -Scon go 1.9.4

Diferencia cero, según la salida del ensamblador.

SteveOC 64
fuente
0

Tuve esta misma pregunta durante mucho tiempo. Así que probé un código aún más simple.

Conclusión: Para tales casos, NO hay diferencia de rendimiento.

Caja de bucle exterior

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

Caja de bucle interior

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

Revisé el archivo compilado en el descompilador de IntelliJ y para ambos casos, obtuve el mismo Test.class

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

También desarme el código para ambos casos usando el método dado en esta respuesta . Solo mostraré las partes relevantes para la respuesta

Caja de bucle exterior

Code:
  stack=2, locals=3, args_size=1
     0: iconst_0
     1: istore_2
     2: iload_2
     3: sipush        1000
     6: if_icmpge     26
     9: iload_2
    10: iconst_2
    11: iadd
    12: istore_1
    13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
    16: iload_1
    17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
    20: iinc          2, 1
    23: goto          2
    26: return
LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13      13     1 intermediateResult   I
            2      24     2     i   I
            0      27     0  args   [Ljava/lang/String;

Caja de bucle interior

Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: sipush        1000
         6: if_icmpge     26
         9: iload_1
        10: iconst_2
        11: iadd
        12: istore_2
        13: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: iload_2
        17: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        20: iinc          1, 1
        23: goto          2
        26: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           13       7     2 intermediateResult   I
            2      24     1     i   I
            0      27     0  args   [Ljava/lang/String;

Si presta mucha atención, solo los Slotasignados iyintermediateResult en LocalVariableTablese intercambian como un producto de su orden de aparición. La misma diferencia en la ranura se refleja en otras líneas de código.

  • No se realiza ninguna operación adicional.
  • intermediateResult sigue siendo una variable local en ambos casos, por lo que no hay diferencia en el tiempo de acceso.

PRIMA

Los compiladores optimizan muchísimo, eche un vistazo a lo que sucede en este caso.

Caso de trabajo cero

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

Cero trabajo descompilado

for(int i = 0; i < 1000; ++i) {
    System.out.println(i);
}
twitu
fuente
-1

Incluso si sé que mi compilador es lo suficientemente inteligente, no me gustaría confiar en él y utilizaré la variante a).

La variante b) tiene sentido para mí solo si necesita desesperadamente hacer el resultado intermedio no esté disponible después del cuerpo del bucle. Pero no puedo imaginar una situación tan desesperada, de todos modos ...

EDITAR: Jon Skeet hizo un muy buen punto, mostrando que la declaración de variables dentro de un ciclo puede hacer una diferencia semántica real.

Abgan
fuente