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