2011-01-10 14 views
30

Estoy leyendo "Better, Faster, Lighter Java" (por Bruce Tate y Justin Gehtland) y estoy familiarizado con los requisitos de legibilidad en equipos de tipo ágil, como lo que Robert Martin discute en sus limpios libros de códigos. En el equipo en el que estoy ahora, se me ha dicho explícitamente que no use el operador + porque crea objetos de cadena adicionales (e innecesarios) durante el tiempo de ejecución.StringBuilder/StringBuffer vs. "+" Operador

Pero este article, escrito en el '04 habla de cómo la asignación de objetos es de aproximadamente 10 instrucciones de la máquina. (esencialmente gratis)

También habla sobre cómo el GC también ayuda a reducir los costos en este entorno.

¿Cuál es la relación real de rendimiento entre el uso de +, StringBuilder o StringBuffer? (En mi caso es StringBuffer solo porque estamos limitados a Java 1.4.2.)

StringBuffer me resulta en un código feo y menos legible, como lo demuestran un par de ejemplos en el libro de Tate. Y StringBuffer está sincronizado con subprocesos que parece tener sus propios costos que superan el "peligro" al usar el operador +.

¿Pensamientos/Opiniones?

+2

queation similar aquí http://stackoverflow.com/questions/4645020/when-to-use-stringbuilder-in-java – Navi

+0

posible duplicado de [StringBuilder frente a la concatenación de cadenas en toString () en Java] (http://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java) –

+0

Posible duplicado de [StringBuilder vs Concatenación de cadenas en toString() en Java] (http://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java) –

Respuesta

41

El uso de String concatenación se traduce en StringBuilder operaciones por el compilador.

Para ver cómo funciona el compilador tomaré una clase de muestra, la compilaré y la descompilaré con jad para ver cuál es el bytecode generado.

clase original:

public void method1() { 
    System.out.println("The answer is: " + 42); 
} 

public void method2(int value) { 
    System.out.println("The answer is: " + value); 
} 

public void method3(int value) { 
    String a = "The answer is: " + value; 
    System.out.println(a + " what is the question ?"); 
} 

La clase descompilada:

public void method1() 
{ 
    System.out.println("The answer is: 42"); 
} 

public void method2(int value) 
{ 
    System.out.println((new StringBuilder("The answer is: ")).append(value).toString()); 
} 

public void method3(int value) 
{ 
    String a = (new StringBuilder("The answer is: ")).append(value).toString(); 
    System.out.println((new StringBuilder(String.valueOf(a))).append(" what is the question ?").toString()); 
} 
  • En method1 el compilador realiza la operación en tiempo de compilación.
  • En method2 la concatenación String es equivalente a usar manualmente StringBuilder.
  • En method3, la concatenación String es definitivamente mala ya que el compilador está creando un segundo StringBuilder en lugar de reutilizar el anterior.

Así que mi regla simple es que las concatenaciones son buenas a menos que sea necesario volver a concatenar el resultado: por ejemplo, en bucles o cuando se necesita almacenar un resultado intermedio.

+3

En la especificación de Java Language, SUGIERE que esto es lo que el compilador haría, dejando abierta la posibilidad de que no lo haga. 15.18.1.2 Optimización de la concatenación de cadenas – avgvstvs

+0

¿Alguna idea de cuánto tiempo esta optimización ha estado vigente en SUN JVM y si existe en IBM JVM? – avgvstvs

+0

Creo que probablemente esté aquí en la JVM de Sun desde la versión 1.4 y no sé si existe en la JVM de IBM. Puede verificar con un descompilador como jad para estar seguro. – gabuzo

20

Su equipo necesita aprender sobre el reasons for avoiding repeated string concatenation.

Ciertamente son momentos en los que tiene sentido utilizar StringBuffer - en particular cuando se va a crear una cadena en un bucle, especialmente si no está seguro de que habrá pocas iteraciones en el bucle. Tenga en cuenta que no solo se trata de crear objetos nuevos; se trata de copiar todos los datos de texto que ya ha agregado. También tenga en cuenta que la asignación de objetos solo es "esencialmente gratuita" si no considera la recolección de basura. Sí, si hay espacio suficiente en la generación actual, básicamente se trata de incrementar un puntero ... pero:

  • Esa memoria debe haberse borrado en algún momento. Eso no es gratis.
  • Está acortando el tiempo hasta que se requiera el siguiente GC. GC no es gratis.
  • Si el objeto vive en la próxima generación, puede tomar más tiempo para ser limpiado - de nuevo, no es libre.

Todas estas cosas son razonablemente barato en que es "generalmente" no vale la pena doblar un diseño alejado de la elegancia para evitar la creación de objetos ... pero no se debe considerar como libre.

Por otra parte, no hay ningún punto en el uso de StringBuffer en los casos en que no se necesitan las cadenas intermedias. Por ejemplo:

String x = a + b + c + d; 

es al menos tan eficientes como:

StringBuffer buffer = new StringBuffer(); 
buffer.append(a); 
buffer.append(b); 
buffer.append(c); 
buffer.append(d); 
String x = buffer.toString(); 
+0

El compilador convierte dos cadenas "Subject" + "Predicate" en – avgvstvs

+0

buf.append ("Subject")) .append ("Predicado"). toString ()? – avgvstvs

+0

@ matt.seil: en realidad, si hay * constantes * de cadena * involucradas, el compilador de Java simplemente usará "Predicado de asunto". Pero sí, usa StringBuffer/StringBuilder detrás de escena de lo contrario. –

5

para pequeñas concatenaciones puede simplemente usar cuerdas y + para facilitar la lectura. El rendimiento no va a sufrir. Pero si usted está haciendo una gran cantidad de operaciones de concatenación ir para StringBuffer.

0

Considérese la siguiente prueba de rendimiento, se está creando una cadena dinámica dentro de un bucle de iteración 100000:

public void constructDynamicString() 
    { 
     long startTime = System.currentTimeMillis(); 
     String s = "test"; 
     //StringBuffer s = new StringBuffer("test"); 
     //StringBuilder s = new StringBuilder("test"); 
     for(int i=0; i<100000; i++) 
     { 
      s += "concat"; 
      //s.append("concat"); 
     } 

     long endTime = System.currentTimeMillis(); 

     System.out.println("Concat time ====== " + (endTime - startTime)); 
    } 

corremos el por encima de la prueba 3 veces, cada enfoque a la vez, y obtenemos el siguiente resultado :

With String, it tooks: 39068 ms 
With StringBuffer, it tooks: 7ms 
With StringBuilder, it tooks: 4ms 

de los resultados anteriores, se observa que el uso de cuerdas objeto para la creación de una cadena de caracteres dinámico es mucho más lento que usar StringBuffer y StringBuilder debido a la inmutabilidad.

Además, StringBuilder es más rápido que StringBuffer ya que no se preocupa por la sincronización, (aunque se ve un poco más rápido, la velocidad sería relativamente diferir si aumentamos el número de iteraciones).

Ahora, con el fin de decidir qué clase que se utiliza, tener en cuenta los siguientes factores:

  1. Si va a crear una cadena de caracteres estática que no debe ser modificado a lo largo del desarrollo del programa, a continuación, utilizar Cadena objeto.
  2. Si está creando una cadena de caracteres dinámicos que necesita ser compartida entre múltiples hilos, entonces considere usar StringBuffer.
  3. Si no existen ambos factores (que es un caso muy común), utilice StringBuilder.

Fuente: StringBuilder VS StringBuffer