Java 101 -String y StringBuilder
Posted: March 24th, 2009 | Author: im8bit | Filed under: Java | No Comments »JM Java 101 – Concatenacion (o como utilizar cadenas para hacer una gran cadena)
Hay una teoria bien difundida que dice no se debe de concatenar utilizando el simbolo de + pues este causa que varios objetos String temporales
sean creados. Se dice en algunos sitios que en su lugar se debe utilizar la clase StringBuilder.
¿Que tan cierto es esto?
He creido conveniente comprobarlo despues de yo mismo pensar esto durante algun tiempo. Utilizaremos para esto la fuente mas confiable que existe,
el codigo generado para la maquina virtual.
Hemos de investigar el fenomeno de concatenacion con un ejemplo creible, uno donde uno de los valores es una variable obtenida “de algun otro lado”,
pues en caso de que todos los valores son definidos, por ejemplo en:
public static void main(String[] args) {
String string4 = "Cadena 1" + "Cadena 2" + "Cadena 3";
}
Java genera codigo optimizado donde todas las cadenas realmente son convertidas a una sola en tiempo de compilacion, generando codigo para la JVM
como sigue:
public static void main(java.lang.String[]); Code: 0: ldc #16; //String Cadena 1Cadena 2Cadena 3 2: astore_1 3: return
Podemos ver aqui que se carga la cadena indicada como #16, y tal cadena es la concatenacion de las 3 diferentes que realmente habiamos especificado.
Intentemos pues con variables de verdad, en los sigiuentes ejemplos, indicare con la variable “value” un valor supuestamente indefinido, aunque
en el siguiente ejemplo esto no tendra mucho sentido, en los sucesivos si lo tendra.
Caso de concatenacion 1.
Crear objetos String para todos las cadenas que deseamos concatenar y finalmente concatenarlas en una sola:
public static void main(String[] args) {
String value = "Valor";
String s1 = "Cadena 1";
String s2 = "Cadena 2";
String s3 = "Cadena 3";
String string1 = s1 + value + s2 + s3;
}
El codigo generado es mas largo pero no por eso mas complicado:
public static void main(java.lang.String[]); Code: 0: ldc #16; //String Valor 2: astore_1 3: ldc #18; //String Cadena 1 5: astore_2 6: ldc #20; //String Cadena 2 8: astore_3 9: ldc #22; //String Cadena 3 11: astore 4 13: new #24; //class java/lang/StringBuilder 16: dup 17: aload_2 18: invokestatic #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 21: invokespecial #32; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 24: aload_1 25: invokevirtual #35; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: aload_3 29: invokevirtual #35; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: aload 4 34: invokevirtual #35; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 37: invokevirtual #39; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 40: astore 5 42: return
La primer sorpresa al ver el codigo es que el mito de concatenar con + no es menos efectivo que utilizar un StringBuilder, pues el compilador
nuevamente realiza algo de optimizacion y utiliza de forma interna un objeto StringBuilder para realizar la concatenacion. Ahorrando simplemente los store
del Caso 1, que no son otra cosa que la inicializacion de los String.
public static void main(String[] args) {
String value = "Valor";
String string2 = "Cadena1" + value + "Cadena2" + "Cadena3";
}
public static void main(java.lang.String[]); Code: 0: ldc #16; //String Valor 2: astore_1 3: new #18; //class java/lang/StringBuilder 6: dup 7: ldc #20; //String Cadena1 9: invokespecial #22; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 12: aload_1 13: invokevirtual #25; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: ldc #29; //String Cadena2 18: invokevirtual #25; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: ldc #31; //String Cadena3 23: invokevirtual #25; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: invokevirtual #33; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 29: astore_2 30: return
Caso 3:
Utilizando un objeto StringBuilder.
Curiosamente esta forma es mas lenta que utilizar el operador +, pues si bien, a primera vista pudiera parecer que no hace proceso extra, si tiene que
hacerlo, si vemos el codigo de la JVM encontramos unos misterioros pop y aload_2, el problema aqui es que StringBuilder.append devuelve un valor,
y una vez que el Thread principal vuelve de la subrutina los valores dejados en el stack tienen que ser removidos aun si no se estan utilizando. Es curioso
que no se optimice esto de la forma en que se hizo en el caso dos, donde no existen los pop, pues de alguna forma el compilador sabe que no se utilizan y
nunca existe un valor de retorno siquiera.
public static void main(String[] args) {
String value = "Valor";
StringBuilder sb = new StringBuilder();
sb.append("Cadena 1");
sb.append("Cadena 2");
sb.append("Cadena 3");
String string3 = sb.toString();
}
public static void main(java.lang.String[]); Code: 0: ldc #16; //String Valor 2: astore_1 3: new #18; //class java/lang/StringBuilder 6: dup 7: invokespecial #20; //Method java/lang/StringBuilder."<init>":()V 10: astore_2 11: aload_2 12: ldc #21; //String Cadena 1 14: invokevirtual #23; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: pop 18: aload_2 19: ldc #27; //String Cadena 2 21: invokevirtual #23; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: pop 25: aload_2 26: ldc #29; //String Cadena 3 28: invokevirtual #23; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: pop 32: aload_2 33: invokevirtual #31; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 36: astore_3 37: return
No hace falta siquiera terminar de interpretar compilado para darse cuenta que la version mas corta es la producida por
String string2 = "Cadena1" + value + "Cadena2" + "Cadena3";
Esto resulta muy interesante, pues gracias a este tipo de optimizaciones no es necesario que java incluya un metodo para dividir una cadena en varias
lineas, incluso, no es necesario pues si solamente estuvieramos utilizando constantes, todos nuestros valores serian concatenados en una sola cadena
en tiempo de compilacion.