java - métodos - stringbuffer vs stringbuilder
Por qué usar StringBuffer en Java en lugar del operador de concatenación de cadenas (18)
Alguien me dijo que es más eficiente usar StringBuffer
para concatenar cadenas en Java que usar el operador +
para String
. ¿Qué pasa debajo del capó cuando haces eso? ¿Qué hace StringBuffer
diferente?
AFAIK depende de la versión de JVM, en las versiones anteriores a la 1.5 con "+" o "+ =" en realidad copiaba toda la cadena cada vez.
Tenga en cuenta que el uso de + = realmente asigna la nueva copia de la cadena.
Como se señaló utilizando bucles + in implica la copia.
Cuando las cadenas que se concatenan son constantes de tiempo de compilación que se concatenan en tiempo de compilación, por lo que
String foo = "a" + "b" + "c";
Ha sido compilado para:
String foo = "abc";
Como las cadenas son imutables en Java, cada vez que concatena una Cadena, se crea un objeto nuevo en la memoria. SpringBuffer usa el mismo objeto en la memoria.
Como se dijo, el objeto String es ummutable, lo que significa que una vez creado (ver a continuación) no se puede cambiar.
String x = new String ("algo"); // o
Cadena x = "algo";
Entonces, cuando intentas concatar objetos String, el valor de esos objetos se toma y se pone en un nuevo objeto String.
Si en su lugar usa StringBuffer, que es mutable, agrega continuamente los valores a una lista interna de caracteres (primitivos), que pueden ampliarse 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.
Creo que dado jdk1.5 (o superior) y su concatenación es segura para subprocesos, debería usar StringBuilder en lugar de StringBuffer http://java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs-stringbuilder.html En cuanto a las ganancias en velocidad: http://www.about280.com/stringtest.html
Personalmente codificaría la legibilidad, así que, a menos que descubra que la concatenación de cadenas hace que el código sea considerablemente más lento, quédese con el método que lo haga más legible.
Creo que la respuesta más simple es: es más rápido.
Si realmente quieres saber todo sobre el tema, siempre puedes echarle un vistazo a la fuente:
Cuando concatena dos cadenas, realmente crea un tercer objeto String en Java. Usar 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 usas uno de sus métodos de agregar (...), no crea una nueva cadena. objeto. En cambio, 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 se hacen muchas concatenaciones de cadenas, verá que usar StringBuffer / Builder es mucho más rápido.
Dado que las cadenas son inmutables, cada llamada al operador + crea un nuevo objeto String y copia los datos de cadena a la nueva cadena. Como copiar un String lleva tiempo lineal en la longitud del String, una secuencia de N llamadas al operador + da como resultado O (N 2 ) tiempo de ejecución (cuadrático).
Por el contrario, como un StringBuffer es mutable, no necesita copiar el String cada vez que ejecuta un Append (), por lo que una secuencia de N Append () llama a O (N) time (linear). Esto solo hace una diferencia significativa en el tiempo de ejecución si está agregando una gran cantidad de cadenas juntas.
Debajo del capó, realmente crea y agrega a StringBuffer, llamando a String () sobre el resultado. Por lo tanto, en realidad ya no importa que uses.
Asi que
String s = "a" + "b" + "c";
se convierte
String s = new StringBuffer().append("a").append("b").append("c").toString();
Eso es cierto para un montón de adjuntos en una única declaración. Si construye su cadena en el transcurso de múltiples instrucciones, entonces está desperdiciando memoria y un StringBuffer o StringBuilder es su mejor opción.
En algunos casos esto es obsoleto debido a las optimizaciones realizadas por el compilador, pero el problema general es ese 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 de bucle):
- construir un objeto de cadena de longitud 1 y valor "x"
- Crea un nuevo objeto de cadena de tamaño 2, copia la antigua cadena "x" en ella, agrega "x" en la posición 2.
- Cree un nuevo objeto de cadena de tamaño 3, copie la cadena anterior "xx" en ella, agregue "x" en la posición 3.
- ... 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 ciclo. Esta es una operación O (n ^ 2). Sin embargo, si supiéramos de antemano que solo necesitábamos N caracteres, podríamos hacerlo en una única asignación, con una copia de solo N caracteres de las cuerdas que estábamos usando, una mera operación O (n).
StringBuffer / StringBuilder lo evitan porque son mutables, por lo que no necesitan seguir copiando los mismos datos una y otra vez (siempre que haya espacio para copiarlos en su búfer interno). Evitan realizar una asignación y una copia proporcional al número de anexos realizados al sobreasignar su memoria intermedia en una proporción de su tamaño actual, agregando O (1) amortizado.
Sin embargo, vale la pena señalar que a menudo el compilador podrá optimizar el código en el estilo de StringBuilder (o mejor, ya que puede realizar doblados constantes, etc.) de forma automática.
Es mejor usar StringBuilder (es una versión no sincronizada, ¿cuándo se crean cadenas en paralelo?) En estos días, en casi todos los casos, pero esto es lo que sucede:
Cuando usas + con dos cadenas, compila un código como este:
String third = first + second;
Para algo como esto:
StringBuilder builder = new StringBuilder( first );
builder.append( second );
third = builder.toString();
Por lo tanto, para pequeños ejemplos, por lo general no hace la diferencia. Pero cuando construyes una cadena compleja, a menudo tienes que lidiar con mucho más que esto; por ejemplo, es posible que esté utilizando muchas sentencias anexas diferentes, o un ciclo 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
- String
s son inmutables) en cada iteración. Esto es muy derrochador Reemplazar esto con un solo StringBuilder
significa que usted solo puede producir un solo String
y no llenar el montón con String
s que no le importa.
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 para cada operador + en la instrucción por separado . Esto significa que hacer a + b + c resultaría en dos construcciones StringBuffer con dos llamadas toString (). Si tuvieras una larga cadena de concats, se convertiría en un verdadero desastre. Hacerlo usted mismo significaba que podía controlar esto y hacerlo correctamente.
Java 5.0 y versiones superiores parecen hacerlo de manera más sensata, por lo que es un problema menor y ciertamente menos detallado.
La clase StringBuffer mantiene una matriz de caracteres para contener los contenidos de las cadenas que concatenas, mientras que el método + crea una nueva cadena cada vez que se invoca y agrega los dos parámetros (param1 + param2).
StringBuffer es más rápido porque 1. podría usar su matriz ya existente para concat / almacenar todas las cadenas. 2. Incluso si no caben en la matriz, es más rápido asignar una matriz de respaldo más grande y luego generar nuevos objetos de cadena para cada evocación.
La sección Operador de concatenación de cadenas + de la Especificación del lenguaje Java proporciona más información de contexto sobre por qué el operador + puede ser tan lento.
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 tanto, 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 ..
}
Para concatenaciones simples como:
String s = "a" + "b" + "c";
No tiene sentido usar StringBuffer
, como señaló jodonnell , se traducirá inteligentemente a:
String s = new StringBuffer().append("a").append("b").append("c").toString();
PERO es muy poco eficaz para concatenar cadenas en un bucle, como:
String s = "";
for (int i = 0; i < 10; i++) {
s = s + Integer.toString(i);
}
El uso de una cadena en este ciclo generará 10 objetos de cadena intermedios en la memoria: "0", "01", "012", etc. Mientras escribes lo mismo usando StringBuffer
, simplemente actualizas un búfer interno de StringBuffer
y no creas esos objetos de cadena intermedios que no necesitas:
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.
Para concatenar dos cadenas usando ''+'', se debe asignar una nueva cadena con espacio para ambas cadenas, y luego los datos copiados de ambas cadenas. Un StringBuffer está optimizado para concatenar, y asigna más espacio de lo necesario inicialmente. Cuando concatena una nueva cadena, en la mayoría de los casos, los caracteres simplemente pueden copiarse al final del búfer de cadena existente.
Para concatenar dos cadenas, el operador ''+'' probablemente tendrá menos sobrecarga, pero a medida que concatene más cadenas, el StringBuffer saldrá adelante, utilizando menos asignaciones de memoria y menos copia de datos.
StringBuffer es mutable. Agrega el valor de la cadena al mismo objeto sin instanciar otro objeto. Haciendo algo como:
myString = myString + "XYZ"
creará un nuevo objeto String.
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 usando el operador "+", los objetos String
intermedios se crearían durante el proceso de construcción de la cadena final.
Sin embargo, como dice JavaDoc para StringBuffer , al menos desde que Java 1.4.2 usa el operador "+" se compila para crear un StringBuffer
y append()
las numerosas cadenas a él. Entonces no hay diferencia, al parecer.
Sin embargo, tenga cuidado al usar la adición de 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;
}
Tenga en cuenta, sin embargo, que generalmente concatenar algunas cadenas con "+" es más limpio que append()
a todas.