vertical setlayout manejo gridlayout dividir como borderlayout java loops optimization

java - manejo - setlayout



¿Por qué se cambia este bucle? (4)

Acabo de encontrar este archivo de clase descompilado de mi clase:

Mi clase

while ((line = reader.readLine()) != null) { System.out.println("line: " + line); if (i == 0) { colArr = line.split(Pattern.quote("|")); } else { i++; } }

El bucle while se ha cambiado a un bucle for en el archivo de clase:

Descompilado MyClass

for (String[] colArr = null; (line = reader.readLine()) != null; ++i) { System.out.println("line: " + line); if (i == 0) { colArr = line.split(Pattern.quote("|")); } else { } }

¿Por qué se ha cambiado este bucle a for ? Creo que podría ser otra forma de optimización de código por el compilador, podría estar equivocado. Solo quería saber si es así, ¿qué ventajas ofrece un bucle for en un bucle while u otro bucle?
¿Cuál es la categoría de tales optimizaciones de código?


Como otros ya han señalado: el descompilador (generalmente) no puede distinguir entre diferentes códigos fuente que dan como resultado el mismo código de bytes.

Desafortunadamente, no proporcionó el código completo del método. Así que lo siguiente contiene algunas conjeturas sobre dónde y cómo aparece este bucle dentro de un método (y estas conjeturas podrían, en cierta medida, distorsionar el resultado).

Pero echemos un vistazo a algunos viajes de ida y vuelta aquí. Considere la siguiente clase, que contiene métodos con las dos versiones del código que publicó:

import java.io.BufferedReader; import java.io.IOException; import java.util.regex.Pattern; public class DecompileExample { public static void methodA(BufferedReader reader) throws IOException { String line = null; int i = 0; while ((line = reader.readLine()) != null) { System.out.println("line: " + line); if (i == 0) { String[] colArr = line.split(Pattern.quote("|")); } else { i++; } } } public static void methodB(BufferedReader reader) throws IOException { String line = null; int i = 0; for (String[] colArr = null; (line = reader.readLine()) != null; ++i) { System.out.println("line: " + line); if (i == 0) { colArr = line.split(Pattern.quote("|")); } else { } } } }

Compilando con

javac DecompileExample.java -g:none

Creará el archivo de clase correspondiente. (Nota: el parámetro -g:none hará que el compilador omita toda la información de depuración. El descompilador podría utilizar la información de depuración para reconstruir una versión más literal del código original, en particular, incluyendo los nombres de las variables originales)

Ahora mirando el código de byte de ambos métodos, con

javap -c DecompileExample.class

cederá lo siguiente:

public static void methodA(java.io.BufferedReader) throws java.io.IOException; Code: 0: aconst_null 1: astore_1 2: iconst_0 3: istore_2 4: aload_0 5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String; 8: dup 9: astore_1 10: ifnull 61 13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 16: new #4 // class java/lang/StringBuilder 19: dup 20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 23: ldc #6 // String line: 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: aload_1 29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 38: iload_2 39: ifne 55 42: aload_1 43: ldc #10 // String | 45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String; 48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 51: astore_3 52: goto 4 55: iinc 2, 1 58: goto 4 61: return

y

public static void methodB(java.io.BufferedReader) throws java.io.IOException; Code: 0: aconst_null 1: astore_1 2: iconst_0 3: istore_2 4: aconst_null 5: astore_3 6: aload_0 7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String; 10: dup 11: astore_1 12: ifnull 60 15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 18: new #4 // class java/lang/StringBuilder 21: dup 22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 25: ldc #6 // String line: 27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: aload_1 31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: iload_2 41: ifne 54 44: aload_1 45: ldc #10 // String | 47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String; 50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 53: astore_3 54: iinc 2, 1 57: goto 6 60: return }

(Hay una pequeña diferencia: La String[] colArr = null se traduce en una

aconst null astore_3

Al comienzo de la segunda versión. Pero este es uno de los aspectos relacionados con partes del código que ha omitido en la pregunta).

No mencionó cuál está utilizando, pero el descompilador JD-GUI de http://jd.benow.ca/ descompila esto en lo siguiente:

import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; import java.util.regex.Pattern; public class DecompileExample { public static void methodA(BufferedReader paramBufferedReader) throws IOException { String str = null; int i = 0; while ((str = paramBufferedReader.readLine()) != null) { System.out.println("line: " + str); if (i == 0) { String[] arrayOfString = str.split(Pattern.quote("|")); } else { i++; } } } public static void methodB(BufferedReader paramBufferedReader) throws IOException { String str = null; int i = 0; String[] arrayOfString = null; while ((str = paramBufferedReader.readLine()) != null) { System.out.println("line: " + str); if (i == 0) { arrayOfString = str.split(Pattern.quote("|")); } i++; } } }

Puede ver que el código es el mismo para ambos casos (al menos en relación con el bucle; hay una diferencia más con respecto a las "variables ficticias" que tuve que introducir para compilarlo, pero esto no está relacionado con la pregunta, por así decirlo).

El mensaje tl; dr es claro:

Se pueden compilar diferentes códigos fuente en el mismo código de bytes. En consecuencia, el mismo código de bytes se puede descomponer en diferentes códigos fuente. Pero cada descompilador tiene que conformarse con una versión del código fuente.

(Una nota al margen: Me sorprendió un poco ver que al compilar sin -g:none (es decir, cuando se retiene la información de depuración), JD-GUI incluso logra reconstruir que el primero usó un while -loop y el segundo usó un for loop. Pero en general, y cuando se omite la información de depuración, esto simplemente ya no es posible).


En esta situación, cambiar while() por for() no es una optimización. Simplemente no hay forma de saber a partir del código de bytes cuál se usó en un código fuente.

Hay muchas situaciones cuando:

while(x)

es lo mismo que:

for(;x;)

Supongamos que tenemos tres aplicaciones java similares: una con la sentencia while() y dos con la correspondiente for() . Primero for() con el criterio de detención solo como en el estándar while() , y segundo for() también con declaración e incremento del iterador.

APLICACIÓN # 1 - FUENTE

public class While{ public static void main(String args[]) { int i = 0; while(i<5){ System.out.println(i); i++; } } }

APLICACIÓN # 2 - FUENTE

public class For{ public static void main(String args[]) { int i = 0; for(; i<5 ;){ System.out.println(i); i++; } } }

APLICACIÓN # 3 - FUENTE

public class For2{ public static void main(String args[]) { for(int i=0;i<5;i++){ System.out.println(i); } } }

Si compilamos todos ellos tenemos:

APLICACIÓN # 1 - BYTECODE

public class While { public While(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: iconst_5 4: if_icmpge 20 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 14: iinc 1, 1 17: goto 2 20: return }

APLICACIÓN # 2 - BYTECODE

public class For { public For(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: iconst_5 4: if_icmpge 20 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 14: iinc 1, 1 17: goto 2 20: return }

APLICACIÓN # 3 - BYTECODE

public class For2 extends java.lang.Object{ public For2(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: iconst_5 4: if_icmpge 20 7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 14: iinc 1, 1 17: goto 2 20: return }

Como puede ver, no hay ninguna diferencia asociada con el uso y while uso.


Eso es básicamente debido a la naturaleza del código de bytes. El código de bytes de Java es algo así como el lenguaje ensamblador, por lo que no hay cosas como for y while loop, simplemente hay instrucciones de salto: goto . Por lo tanto, puede que no haya diferencia entre el while y el bucle, ambos pueden compilarse a un código similar y el descompilador simplemente hace conjeturas.


Tanto el segmento for bucle for como el de while se pueden traducir a un código de máquina similar. Después de eso, al descompilar, el descompilador debe elegir uno de los two possible escenarios two possible .

Supongo que eso es lo que está pasando aquí.

simplemente:

compile(A) -> C compile(B) -> C

Entonces, cuando te den C , deberías elegir a A o B