viernes, 21 de mayo de 2010

operador +=, cadenas, cadenas constante y el compilador en Java

Como se vio en la entrada anterior, el utilizar incorrectamente un operador, puede ser optimizado por el compilador o en su defecto, una instruccion (en apariencia) se puede convertir en un conjunto de instrucciones, que impacta negarivamente en el desempeño del sistema.

El operador += es un ejemplo de operador que nunca se debe utilizar, debido a como esta implementado, veamos este codigo.






1: package test;
2: 
3: public class StringTest {
4:         public static void main(String[] args) {
5:                 String x = "cadena1";
6:                 x += "cadena2";
7:                 System.out.println(x);
8:         }
9: }
10: 

Si de compilamos el codigo java resultante (el archivo .class) con el comando "javap -c", obtenemos lo siguiente:

Compiled from "StringTest.java"
   public class test.StringTest extends java.lang.Object{
   public test.StringTest();
   Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

   public static void main(java.lang.String[]);
   Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: new #18; //class java/lang/StringBuilder
   6: dup
   7: aload_1
   8: invokestatic #20; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   11: invokespecial #26; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   14: ldc #29; //String cadena2
   16: invokevirtual #31; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   19: invokevirtual #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   22: astore_1
   23: getstatic #39; //Field java/lang/System.out:Ljava/io/PrintStream;
   26: aload_1
   27: invokevirtual #45; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   30: return
   }

El codigo equivalente, para generar el mismo resultado, pero utilizando explicitamente la clase StringBuilder y la funcion valueOf, es el siguiente:

1: package test;
2: 
3: public class StringTest2 {
4:         public static void main(String[] args) {
5:                 String x = "cadena1";
6:                 x = new StringBuilder(String.valueOf(x)).append("cadena2").toString();
7:                 System.out.println(x);
8:         }
9: }
10: 

Pero que ocurre cuando se mete la operacion += dentro de un loop? Como en el siguiente codigo:

01: package test;
02: 
03: public class StringTest3 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 for (int i = 0; i < 10; i++) {
07:                         x += "cadena2";
08:                 }
09:         }
10: }
11: 

Al decompilar, se genera el siguiente codigo:

Compiled from "StringTest3.java"
   public class test.StringTest3 extends java.lang.Object{
   public test.StringTest3();
  Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
  Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: iconst_0
   4: istore_2
   5: goto 31
   8: new #18; //class java/lang/StringBuilder
   11: dup
   12: aload_1
   13: invokestatic #20; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   16: invokespecial #26; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   19: ldc #29; //String cadena2
   21: invokevirtual #31; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24: invokevirtual #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27: astore_1
   28: iinc 2, 1
   31: iload_2
   32: bipush 10
   34: if_icmplt 8
   37: return

}

Es decir, el codigo equivalente es el siguiente:

01: package test;
02: 
03: public class StringTest4 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 for (int i = 0; i < 10; i++) {
07:                         x = new StringBuilder(String.valueOf(x)).append("cadena2").toString();
08:                 }
09:         }
10: }
11: 

Como se puede observar, en cada iteracion, se construye un nuevo objeto StringBuilder, con el resultado de ejecutar la funcion valueOf, que recibe como parametro la variable x, posterior mente, se agrega la cadena que "cadena2" y finalmente, se convierte en una cadena nueva, la cual se asigna a la variable x.

Derivado de esto es destacable notar que este codigo es ineficiente, en la mayoria de las situaciones, particularmente cuando se emplea dentro de loops.

Sin embargo, cuando se emplea en conjunto con el operador +, puede eficiente, pero solo si se conoce exactamente la cantidad de elemenos implicados en la operacion. Como en el siguiente ejemplo:

01: package test;
02: 
03: public class StringTest5 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 String y = "auxiliar";
07:                 x += "cadena2" + y;
08:                 System.out.println(x);
09:         }
10: }
11: 

Al de compilar se obtiene lo siguiente:

Compiled from "StringTest5.java"
   public class test.StringTest5 extends java.lang.Object{
   public test.StringTest5();
   Code:
   0: aload_0
   1: invokespecial #8; //Method java/lang/Object."<init>":()V
   4: return

public static void main(java.lang.String[]);
   Code:
   0: ldc #16; //String cadena1
   2: astore_1
   3: ldc #18; //String auxiliar
   5: astore_2
   6: new #20; //class java/lang/StringBuilder
   9: dup
   10: aload_1
   11: invokestatic #22; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   14: invokespecial #28; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   17: ldc #31; //String cadena2
   19: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22: aload_2
   23: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   26: invokevirtual #37; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   29: astore_1
   30: getstatic #41; //Field java/lang/System.out:Ljava/io/PrintStream;
   33: aload_1
   34: invokevirtual #47; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   37: return
}

El codigo equivalente, es el siguiente:

01: package test;
02: 
03: public class StringTest6 {
04:         public static void main(String[] args) {
05:                 String x = "cadena1";
06:                 String y = "auxiliar";
07:                 x = new StringBuilder(String.valueOf(x)).append("cadena2").append(y)
08:                                 .toString();
09:                 System.out.println(x);
10:         }
11: }
12: 

Lo cual es eficiente para en terminos de uso de los objetos creados.

No hay comentarios.:

Publicar un comentario