web-development-kb-es.site

¿Por qué utilizar StringBuffer en Java en lugar del operador de concatenación de cadenas?

Alguien me dijo que es más eficiente usar StringBuffer para concatenar cadenas en Java que usar el operador + para Strings. ¿Qué pasa bajo el capó cuando haces eso? ¿Qué hace StringBuffer diferente?

53
harry

Es mejor usar StringBuilder (es una versión no sincronizada; ¿cuándo construyes cadenas en paralelo?) En estos días, en casi todos los casos, pero esto es lo que sucede:

Cuando usas + con dos cadenas, compila código como este:

String third = first + second;

A algo como esto:

StringBuilder builder = new StringBuilder( first );
builder.append( second );
third = builder.toString();

Por lo tanto, por pequeños ejemplos, por lo general no hace una diferencia. Pero cuando estás construyendo una cadena compleja, a menudo tienes que lidiar con mucho más que esto; por ejemplo, podría estar usando muchas declaraciones adjuntas diferentes, o un bucle como este:

for( String str : strings ) {
  out += str;
}

En este caso, se requiere una nueva instancia de StringBuilder, y una nueva String (el nuevo valor de out - Strings es inmutable) en cada iteración. Esto es muy inútil. Reemplazar esto con una única StringBuilder significa que solo puede producir una única String y no llenar el montón con Strings que no le interesan.

61
Calum

Para concatenaciones simples como:

String s = "a" + "b" + "c";

Es bastante inútil usar StringBuffer - como jodonnell señalado que se traducirá inteligentemente en:

String s = new StringBuffer().append("a").append("b").append("c").toString();

PEROno tiene mucho rendimiento para concatenar cadenas en un bucle, como:

String s = "";
for (int i = 0; i < 10; i++) {
    s = s + Integer.toString(i);
}

El uso de cadenas en este bucle generará 10 objetos de cadenas intermedias en la memoria: "0", "01", "012" y así sucesivamente. Mientras escribe lo mismo usando StringBuffer, simplemente actualiza un búfer interno de StringBuffer y no crea los objetos de cadena intermedios que no necesita:

StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
    sb.append(i);
}

En realidad, para el ejemplo anterior, debe usar StringBuilder (introducido en Java 1.5) en lugar de StringBuffer - StringBuffer es un poco más pesado ya que todos sus métodos están sincronizados.

42
tkokoszka

Uno no debería ser más rápido que el otro. Esto no era cierto antes de Java 1.4.2, porque al concatenar más de dos cadenas utilizando el operador "+", se crearían objetos intermedios String durante el proceso de creación de la cadena final.

Sin embargo, como el JavaDoc para StringBuffer indica, al menos desde que Java 1.4.2 utiliza el operador "+" compila a la hora de crear un StringBuffer y append()ing las muchas cadenas para él. Así que no hay diferencia, al parecer.

Sin embargo, ¡tenga cuidado al agregar una cadena a otra dentro de un bucle! Por ejemplo:

String myString = "";

for (String s : listOfStrings) {
  // Be careful! You're creating one intermediate String object
  // for every iteration on the list (this is costly!)
  myString += s;
}

Sin embargo, tenga en cuenta que, por lo general, concatenar algunas cadenas con "+" es más limpio que append()ing todos ellos.

20
André Chalella

Bajo el capó, en realidad crea y se agrega a un StringBuffer, llamando a toString () en el resultado. Así que en realidad ya no importa cuál uses.

Asi que

String s = "a" + "b" + "c";

se convierte en

String s = new StringBuffer().append("a").append("b").append("c").toString();

Eso es cierto para un montón de apéndices en línea dentro de una sola declaración. Si construyes tu cadena a lo largo de varias declaraciones, entonces estás desperdiciando memoria y un StringBuffer o StringBuilder es tu mejor opción.

9
jodonnell

Creo que dado jdk1.5 (o mayor) y su concatenación es segura para subprocesos, debe usar StringBuilder en lugar de StringBuffer http://Java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs-stringbuilder .html En cuanto a las ganancias de velocidad: http://www.about280.com/stringtest.html

Personalmente, codificaría para facilitar la lectura, de modo que, a menos que encuentre que la concatenación de cadenas hace que su código sea considerablemente más lento, siga con el método que lo haga más legible.

7
slipset

En algunos casos, esto es obsoleto debido a las optimizaciones realizadas por el compilador, pero el problema general es que el código como:

string myString="";
for(int i=0;i<x;i++)
{
    myString += "x";
}

actuará de la siguiente manera (cada paso es la siguiente iteración del bucle):

  1. construir un objeto de cadena de longitud 1 y valor "x"
  2. Cree un nuevo objeto de cadena de tamaño 2, copie la cadena antigua "x" en él, agregue "x" en la posición 2.
  3. Cree un nuevo objeto de cadena de tamaño 3, copie la cadena antigua "xx" en él, agregue "x" en la posición 3.
  4. ... y así

Como puede ver, cada iteración tiene que copiar un carácter más, lo que nos lleva a realizar 1 + 2 + 3 + 4 + 5 + ... + N operaciones en cada bucle. Esta es una operación O (n ^ 2). Sin embargo, si supiéramos por adelantado que solo necesitábamos N caracteres, podríamos hacerlo en una sola asignación, con una copia de solo N caracteres de las cadenas que estábamos usando, una mera O(n) operación.

StringBuffer/StringBuilder evita esto porque son mutables, por lo que no es necesario que sigas copiando los mismos datos una y otra vez (siempre que haya espacio para copiar en su búfer interno). Evitan realizar una asignación y una copia proporcional al número de anexos realizados por la asignación excesiva de su búfer en una proporción de su tamaño actual, dando amortización O(1) anexa.

Sin embargo, vale la pena señalar que a menudo el compilador podrá optimizar el código en el estilo de StringBuilder (o mejor dicho, ya que puede realizar el plegado constante, etc.) automáticamente.

5
Brian

AFAIK depende de la versión de JVM, en versiones anteriores a 1.5 usando "+" o "+ =" en realidad copió toda la cadena cada vez.

Tenga en cuenta que el uso de + = realmente asigna la nueva copia de la cadena.

Como se señaló con + en bucles implica copiar.

Cuando las cadenas conactadas son constantes de tiempo de compilación concatenadas en tiempo de compilación, entonces

String foo = "a" + "b" + "c";

Se compila para:

String foo = "abc"; 
3
jb.

Java convierte string1 + string2 en una construcción StringBuffer, append () y toString (). Esto tiene sentido.

Sin embargo, en Java 1.4 y anteriores, haría esto por cada operador + en la declaración por separado . Esto significaba que hacer a + b + c daría lugar a dos construcciones StringBuffer con dos toString () llamadas. Si tuvieras una larga cadena de concats, se convertiría en un verdadero desastre. Hacerlo tú mismo significaba que podías controlar esto y hacerlo correctamente.

Java 5.0 y superior parecen hacerlo de manera más sensata, por lo que es un problema menor y ciertamente es menos detallado.

3
Alan Krueger

Más información:

StringBuffer es una clase segura para subprocesos


public final class StringBuffer extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
     public synchronized StringBuffer append(StringBuffer stringbuffer)
    {
        super.append(stringbuffer);
        return this;
    }
// .. skip ..
}

Pero StringBuilder no es seguro para subprocesos, por lo que es más rápido usar StringBuilder si es posible


public final class StringBuilder extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
    public StringBuilder append(String s)
    {
        super.append(s);
        return this;
    }
// .. skip ..
}

1
Eric Yung

StringBuffer es mutable. Agrega el valor de la cadena al objeto same sin crear una instancia de otro objeto. Haciendo algo como:

myString = myString + "XYZ"

creará un nuevo objeto String.

1
Loren Segal

Para concatenar dos cadenas usando '+', una nueva cadena debe asignarse con espacio para ambas cadenas, y luego los datos se copian de ambas cadenas. Un StringBuffer está optimizado para concatenar y asigna más espacio del necesario inicialmente. Cuando concatena una nueva cadena, en la mayoría de los casos, los caracteres pueden simplemente copiarse al final del búfer de cadenas existente.
Para concatenar dos cadenas, el operador '+' probablemente tendrá menos gastos generales, pero a medida que concatene más cadenas, el StringBuffer saldrá adelante, usando menos asignaciones de memoria y menos copia de datos.

1
Eclipse

La clase StringBuffer mantiene una matriz de caracteres para mantener el contenido de las cadenas que concatena, mientras que el método + crea una nueva cadena cada vez que se llama y agrega los dos parámetros (param1 + param2).

El StringBuffer es más rápido porque 1. podría ser capaz de usar su matriz ya existente para concat/almacenar todas las cadenas. 2. Incluso si no encajan en la matriz, es más rápido asignar una matriz de respaldo más grande que generar nuevos objetos String para cada evocación.

1
Matt Novinger

Debido a que las cadenas son inmutables, cada llamada al operador + crea un nuevo objeto de cadena y copia los datos de la cadena a la nueva cadena. Dado que copiar una cadena lleva un tiempo lineal en la longitud de la cadena, una secuencia de N llamadas al operador + da como resultado O (N2) tiempo de ejecución (cuadrático).

A la inversa, dado que un StringBuffer es mutable, no necesita copiar la Cadena cada vez que realiza un Append (), por lo que una secuencia de N Append () toma O(N) tiempo (lineal). Esto solo hace una diferencia significativa en el tiempo de ejecución si está agregando un gran número de cadenas juntas.

1
Adam Rosenfield

Como se dijo, el objeto String es ummutable, lo que significa que una vez que se crea (ver más abajo) no se puede cambiar.

Cadena x = nueva cadena ("algo"); // o

Cadena x = "algo";

Por lo tanto, cuando intenta concanear objetos String, el valor de esos objetos se toma y se coloca en un nuevo objeto String.

Si, en cambio, utiliza el StringBuffer, que IS mutable, continuamente agrega los valores a una lista interna de char (primitivas), que puede extenderse o truncarse para ajustarse al valor necesario. No se crean nuevos objetos, solo se crean/eliminan nuevos caracteres cuando es necesario para mantener los valores.

1
Christian P.

Cuando concatena dos cadenas, en realidad creas un tercer objeto String en Java. El uso de StringBuffer (o StringBuilder en Java 5/6) es más rápido porque usa una matriz interna de caracteres para almacenar la cadena, y cuando usa uno de sus métodos de agregar (...), no crea una nueva cadena objeto. En su lugar, StringBuffer/Buider agrega la matriz interna.

En concatenaciones simples, no es realmente un problema si concatena cadenas usando StringBuffer/Builder o el operador '+', pero cuando haces muchas concatenaciones de cadenas, verás que usar un StringBuffer/Builder es mucho más rápido.

1
Alexandre Brasil

Debido a que las cadenas son inmutables en Java, cada vez que concanate una cadena, se crea un nuevo objeto en la memoria. StringBuffer usa el mismo objeto en la memoria.

0
Ivan Bosnic

Creo que la respuesta más simple es: es más rápido.

Si realmente quieres saber todo lo que hay debajo del capó, siempre puedes echar un vistazo a la fuente:

http://www.Sun.com/software/opensource/Java/getinvolved.jsp

http://download.Java.net/jdk6/latest/archive/

0
rgcb

La sección Operador de concatenación de cadenas + de la Especificación del lenguaje Java le brinda más información de fondo sobre por qué el operador + puede ser tan lento.

0
Benedikt Waldvogel