Concatenando Strings en Java
Posted by Paco Ros en 4 \04\+01:00 marzo \04\+01:00 2007
Es un truco muy conocido y lo recuerdan en esta entrada de javaHispano.
Cuando se usa el operador «+» con dos instancias de String, el compilador, lo que hace es sustituir la operación por la creación de un StringBuffer.
En el ejemplo del artículo aparecen estos dos fragmentos de código:
// Caso ineficiente (pero típico) long start = System.currentTimeMillis(); String str = ""; for(int i = 0; i < 10000; i++) str += i; System.out.println((System.currentTimeMillis() - start) / 1000.0d); // Caso eficiente start = System.currentTimeMillis(); StringBuffer strb = new StringBuffer(); for(int i = 0; i < 10000; i++) strb.append( i ); System.out.println((System.currentTimeMillis() - start) / 1000.0d);
En el primer fragmento de código, el compilador sustituye el contenido del bucle con un código similar al siguiente:
for (int i=0; i<10000; i++){ StringBuffer sb = new StringBuffer(str); sb.append(i); String straux = sb.toString(); str = sb.toString(); }
A cada iteración se debe ubicar en memoria un nuevo objeto y ese objeto tiene un tamaño cada vez mayor.
La creación dinámica de memoria es una de las operaciones más costosas en Java (y en la mayoría de lenguajes que conozco) y la creación de 10000 instancias de StringBuffer hace que el mismo código resulte mucho más lento.
Ejecutando el código comparativo en mi portátil obtengo 3.277s para ejecutar el bucle con la versión ineficiente y 0.004s en la versión eficiente.
Podéis comprobarlo personalmente copiando y pegando este codigo:
public class Test{ public static void main(String[] args){ long start; String str; StringBuffer strb; // Usando el operador "+" str = ""; start = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) str += i; System.out.println((System.currentTimeMillis() - start) / 1000.0d); // Usando una instancia de StringBuffer y el método append() strb = new StringBuffer(); start = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) strb.append(i); System.out.println((System.currentTimeMillis() - start) / 1000.0d); // Reroducción de lo que hace el compilador en el caso 1 str = ""; start = System.currentTimeMillis(); for (int i=0; i<10000; i++){ StringBuffer sbaux = new StringBuffer(); sbaux.append(str); sbaux.append(i); String straux = sbaux.toString(); str = straux; } System.out.println((System.currentTimeMillis() - start) / 1000.0d); } }
guillem said
Promig de tres execucions: 41.342s.
Promig de tres execucions: 0.173s.
Promig de tres execucions: 2.104s.
Promig de tres execucions: 0.054s.
(Comentari editat per tal de veure bé el codi ;-))
guillem said
Otia quin xurro… bé, ho deixaré a http://bulma.net/beowulf/misc/strings.txt
Tribe said
Vaya, qué sorpresa. Del orden de 250 veces más rápido en Java y 40 veces en Ruby según los resultados de Guillem, bestial.
Por otra parte impresionante el resaltado de sintaxis en Kate de KDE 3.5.6 al abrir el fichero de texto de Guillem.
Ah, muy interesantes estas entradas Paco sobre Java, a ver si te animas a poner más ahora que es(?) GPL, como decías en otra entrada :)
Salud!
Enrique said
Que bueno justo buscaba como concatenar un int a un string en ruby muchas gracias.