Entiendo la diferencia entre String
y StringBuilder
(StringBuilder
ser mutable) pero ¿hay una gran diferencia de rendimiento entre los dos?
El programa en el que estoy trabajando tiene una gran cantidad de adjuntos de cadenas basadas en mayúsculas y minúsculas (500+). ¿Es el uso de StringBuilder
una mejor opción?
Sí, la diferencia de rendimiento es significativa. Consulte el artículo de KB " Cómo mejorar el rendimiento de la concatenación de cadenas en Visual C # ".
Siempre he intentado codificar la claridad primero y luego optimizar el rendimiento más tarde. ¡Eso es mucho más fácil que hacerlo al revés! Sin embargo, habiendo visto la enorme diferencia de rendimiento en mis aplicaciones entre las dos, ahora lo pienso con un poco más de cuidado.
Afortunadamente, es relativamente sencillo ejecutar el análisis de rendimiento en su código para ver dónde está gastando el tiempo y luego modificarlo para usar StringBuilder
donde sea necesario.
Para aclarar lo que Gillian dijo sobre 4 cuerdas, si tienes algo como esto:
string a,b,c,d;
a = b + c + d;
entonces sería más rápido usar cadenas y el operador más. Esto se debe a que (como Java, como señala Eric), utiliza StringBuilder internamente de manera automática (en realidad, usa una primitiva que también usa StringBuilder)
Sin embargo, si lo que estás haciendo está más cerca de:
string a,b,c,d;
a = a + b;
a = a + c;
a = a + d;
Entonces necesitas usar explícitamente un StringBuilder. .Net no crea automáticamente un StringBuilder aquí, porque sería inútil. Al final de cada línea, "a" debe ser una cadena (inmutable), por lo que tendría que crear y disponer un StringBuilder en cada línea. Para la velocidad, deberías usar el mismo StringBuilder hasta que hayas terminado de construir:
string a,b,c,d;
StringBuilder e = new StringBuilder();
e.Append(b);
e.Append(c);
e.Append(d);
a = e.ToString();
StringBuilder es preferibleSIestás haciendo varios bucles, o forks en tu código pasa ... sin embargo, para el rendimiento PURE, si puedes salirse con una declaración de cadenaSINGLE, entonces eso Es mucho más performante.
Por ejemplo:
string myString = "Some stuff" + var1 + " more stuff"
+ var2 + " other stuff" .... etc... etc...;
es más performante que
StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..
En este caso, StringBuild podría considerarse más mantenible, pero no es más eficaz que la declaración de cadena única.
9 de cada 10 veces ... use el generador de cadenas.
En una nota al margen: string + var también es más eficaz que el enfoque string.Format (generalmente) que usa un StringBuilder internamente (cuando tenga dudas ... ¡verifique el reflector!)
Este punto de referencia muestra que la concatenación regular es más rápida cuando se combinan 3 o menos cadenas.
http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/
StringBuilder puede hacer una mejora muy significativa en el uso de la memoria, especialmente en el caso de agregar 500 cadenas juntas.
Considere el siguiente ejemplo:
string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
buffer += i.ToString();
}
return buffer;
¿Qué pasa en la memoria? Se crean las siguientes cadenas:
1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"
¡Al agregar esos cinco números al final de la cadena, creamos 13 objetos de cadena! ¡Y 12 de ellos fueron inútiles! ¡Guauu!
StringBuilder soluciona este problema. No es una "cadena mutable" como a menudo escuchamos ( todas las cadenas en .NET son inmutables ). Funciona manteniendo un búfer interno, una matriz de caracteres. Al llamar a Append () o AppendLine () se agrega la cadena al espacio vacío al final de la matriz de caracteres; si la matriz es demasiado pequeña, crea una nueva matriz más grande y copia el búfer allí. Entonces, en el ejemplo anterior, StringBuilder podría necesitar solo una matriz para contener las 5 adiciones a la cadena, dependiendo del tamaño de su búfer. Puedes decirle a StringBuilder qué tan grande debe ser su búfer en el constructor.
Un ejemplo sencillo para demostrar la diferencia de velocidad cuando se usa String
concatenación vs StringBuilder
:
System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");
Resultado:
Usando la concatenación de cadenas: 15423 milisegundos
StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");
Resultado:
Utilizando StringBuilder: 10 milisegundos
Como resultado, la primera iteración tomó 15423 ms, mientras que la segunda iteración con StringBuilder
tomó 10 ms.
Me parece que usar StringBuilder
es más rápido, mucho más rápido.
Sí, StringBuilder
ofrece un mejor rendimiento al realizar operaciones repetidas en una cadena. Se debe a que todos los cambios se realizan en una sola instancia, por lo que puede ahorrar mucho tiempo en lugar de crear una nueva instancia como String
.
String
System
espacio de nombresStringBuilder
(cadena mutable)
System.Text
espacio de nombresRecomiendo encarecidamente el artículo dotnet mob: String Vs StringBuilder en C # .
Pregunta relacionada sobre el desbordamiento de pila: ¿La mutabilidad de la cadena cuando la cadena no cambia en C #? .
StringBuilder reduce el número de asignaciones y asignaciones, a un costo de memoria extra utilizada. Si se usa correctamente, puede eliminar completamente la necesidad de que el compilador asigne cadenas cada vez más grandes una y otra vez hasta que se encuentre el resultado.
string result = "";
for(int i = 0; i != N; ++i)
{
result = result + i.ToString(); // allocates a new string, then assigns it to result, which gets repeated N times
}
vs.
String result;
StringBuilder sb = new StringBuilder(10000); // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
sb.Append(i.ToString()); // fill the buffer, resizing if it overflows the buffer
}
result = sb.ToString(); // assigns once
El rendimiento de una operación de concatenación para un objeto String o StringBuilder depende de la frecuencia con la que se produce una asignación de memoria. Una operación de concatenación de cadenas siempre asigna memoria, mientras que una operación de concatenación de cadenas solo asigna memoria si el búfer de objetos StringBuilder es demasiado pequeño para acomodar los nuevos datos. En consecuencia, la clase String es preferible para una operación de concatenación si se concatena un número fijo de objetos String. En ese caso, las compilaciones pueden incluso combinar las operaciones de concatenación individuales en una sola operación. Un objeto StringBuilder es preferible para una operación de concatenación si se concatena un número arbitrario de cadenas; por ejemplo, si un bucle concatena un número aleatorio de cadenas de entrada del usuario.
Fuente: MSDN
String Vs String Builder:
Lo primero que debes saber es en qué Asamblea viven estas dos clases.
Asi que,
string está presente en el espacio de nombres System
.
y
StringBuilder está presente en el espacio de nombres System.Text
.
Para cadena declaración:
Tienes que incluir el espacio de nombres System
. algo como esto. Using System;
y
Para StringBuilder declaración:
Tienes que incluir el espacio de nombres System.text
. algo como esto. Using System.text;
Ahora viene la pregunta real.
¿Cuál es la diferencia entre string & StringBuilder ?
La principal diferencia entre estos dos es que:
cadena es inmutable.
y
StringBuilder es mutable.
Así que ahora vamos a discutir la diferencia entre inmutable y mutable
Mutable: : significa cambiable.
Inmutable: : significa No cambiable.
Por ejemplo:
using System;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// String Example
string name = "Rehan";
name = name + "Shah";
name = name + "RS";
name = name + "---";
name = name + "I love to write programs.";
// Now when I run this program this output will be look like this.
// output : "Rehan Shah RS --- I love to write programs."
}
}
}
Así que en este caso vamos a cambiar el mismo objeto 5 veces.
Así que la pregunta obvia es esa! Lo que realmente sucede debajo del capó, cuando cambiamos la misma cadena 5 veces.
Esto es lo que sucede cuando cambiamos la misma cadena 5 veces.
miremos la figura.
Explicación:
Cuando iniciamos por primera vez esta variable "nombre" a "Rehan" i-e string name = "Rehan"
esta variable se crea en la pila "nombre" y señala ese valor de "Rehan". después de ejecutar esta línea: "nombre = nombre +" Shah ". la variable de referencia ya no apunta a ese objeto" Rehan ", ahora apunta a" Shah "y así sucesivamente.
Así que string
es un significado inmutable que una vez que creamos el objeto en la memoria no podemos cambiarlo.
Entonces, cuando concatinamos la variable name
, el objeto anterior permanece allí en la memoria y se crea otro nuevo objeto de cadena ...
Así que de la figura anterior tenemos cinco objetos, los cuatro objetos son desechados y no se usan en absoluto. Aún permanecen en la memoria y ocupan la cantidad de memoria. El "recolector de basura" es responsable de que limpie los recursos de la memoria.
Entonces, en el caso de una cadena en cualquier momento en que manipulemos la cadena una y otra vez, tenemos muchos objetos creados y que permanecen allí en la memoria.
Así que esta es la historia de la cadena Variable.
Ahora veamos hacia el objeto StringBuilder. Por ejemplo:
using System;
using System.Text;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// StringBuilder Example
StringBuilder name = new StringBuilder();
name.Append("Rehan");
name.Append("Shah");
name.Append("RS");
name.Append("---");
name.Append("I love to write programs.");
// Now when I run this program this output will be look like this.
// output : "Rehan Shah Rs --- I love to write programs."
}
}
}
Así que en este caso vamos a cambiar el mismo objeto 5 veces.
Así que la pregunta obvia es esa! Lo que realmente sucede debajo del capó, cuando cambiamos el mismo StringBuilder 5 veces.
Esto es lo que sucede cuando cambiamos el mismo StringBuilder 5 veces.
Explicación: En el caso del objeto StringBuilder. no obtendrías el nuevo objeto El mismo objeto cambiará en la memoria, por lo que incluso si cambia el objeto y dice 10,000 veces, solo tendremos un único objeto stringBuilder.
No tiene una gran cantidad de objetos de basura o de objetos stringBuilder no referenciados porque puede cambiarse. ¿Es mutable lo que significa que cambia con el tiempo?
Diferencias:
StringBuilder
es mejor para construir una cadena de muchos valores no constantes.
Si está creando una cadena a partir de una gran cantidad de valores constantes, como varias líneas de valores en un documento HTML o XML u otros trozos de texto, puede simplemente agregar la misma cadena, porque casi todos los compiladores sí lo hacen. "plegado constante", un proceso de reducción del árbol de análisis cuando tienes un montón de manipulación constante (también se usa cuando escribes algo como int minutesPerYear = 24 * 365 * 60
). Y para casos simples con valores no constantes anexados entre sí, el compilador .NET reducirá su código a algo similar a lo que hace StringBuilder
.
Pero cuando el apilador no puede reducir su apéndice a algo más simple, querrá una StringBuilder
. Como señala fizch, es más probable que ocurra dentro de un bucle.
Considerar ' La tragedia triste del teatro de micro-optimización '.
El uso de cadenas para la concatenación puede llevar a una complejidad de tiempo de ejecución del orden de O(n^2)
.
Si usa una StringBuilder
, hay que copiar mucho menos la memoria. Con la StringBuilder(int capacity)
puede aumentar el rendimiento si puede estimar qué tan grande será la String
final. Incluso si no eres preciso, probablemente solo tengas que aumentar la capacidad de StringBuilder
un par de veces, lo que también puede ayudar al rendimiento.
He visto mejoras significativas en el rendimiento al usar la llamada al método EnsureCapacity(int capacity)
en una instancia de StringBuilder
antes de usarla para cualquier almacenamiento de cadenas. Normalmente lo llamo en la línea de código después de la instanciación. Tiene el mismo efecto que si instanciara la StringBuilder
de esta manera:
var sb = new StringBuilder(int capacity);
Esta llamada asigna la memoria necesaria con anticipación, lo que causa menos asignaciones de memoria durante múltiples operaciones Append()
. Tiene que hacer una conjetura sobre la cantidad de memoria que necesitará, pero para la mayoría de las aplicaciones esto no debería ser demasiado difícil. Por lo general me equivoco en el lado de un poco demasiado de memoria (estamos hablando de 1k o menos).
Creo que StringBuilder es más rápido si tiene más de 4 cadenas que debe adjuntar. Además, puede hacer algunas cosas interesantes como AppendLine.
En .NET, StringBuilder sigue siendo más rápido que las cadenas anexas. Estoy bastante seguro de que en Java, simplemente crean un StringBuffer debajo del capó cuando añades cadenas, así que no hay realmente una diferencia. No estoy seguro de por qué no han hecho esto en .NET todavía.
String y StringBuilder son en realidad inmutables, StringBuilder ha incorporado buffers que permiten que su tamaño sea administrado de manera más eficiente. Cuando StringBuilder necesita cambiar de tamaño es cuando se vuelve a asignar en el montón. Por defecto, tiene un tamaño de 16 caracteres, puede configurarlo en el constructor.
p.ej.
StringBuilder sb = new StringBuilder (50);
Además de las respuestas anteriores, lo primero que siempre hago cuando pienso en temas como este es crear una pequeña aplicación de prueba. Dentro de esta aplicación, realice una prueba de tiempo para ambos escenarios y vea por usted mismo cuál es más rápido.
En mi humilde opinión, agregar más de 500 entradas de cadena definitivamente debería usar StringBuilder.
La concatenación de cuerdas te costará más. En Java, puede usar StringBuffer o StringBuilder según su necesidad. Si desea una implementación sincronizada y segura para subprocesos, vaya a StringBuffer. Esto será más rápido que la concatenación de cadenas.
Si no necesita una implementación segura o sincronizada con Thread, vaya a StringBuilder. Esto será más rápido que la concatenación de cadenas y también más rápido que StringBuffer, ya que no hay sobrecarga de sincronización.
StringBuilder
es significativamente más eficiente, pero no verá ese rendimiento a menos que esté realizando una gran cantidad de modificación de cadena.
A continuación se muestra un fragmento rápido de código para dar un ejemplo del rendimiento. Como puede ver, en realidad solo comienza a ver un aumento importante en el rendimiento cuando entra en grandes iteraciones.
Como puede ver, las 200,000 iteraciones tomaron 22 segundos, mientras que las 1 millón de iteraciones que usaron la StringBuilder
fueron casi instantáneas.
string s = string.Empty;
StringBuilder sb = new StringBuilder();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 50000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 200000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());
for (int i = 0; i <= 1000000; i++)
{
sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());
Console.ReadLine();
Resultado del código anterior:
Cadena de inicio + al 28/01/2013 16:55:40.
Cadena terminada + al 28/01/2013 16:55:40.
Cadena de inicio + al 28/01/2013 16:55:40.
Cadena terminada + al 28/01/2013 16:56:02.
A partir de la Sb, agregue el 28/01/2013 16:56:02.
Terminado Sb anexar el 28/01/2013 16:56:02.
Como regla general, si tengo que establecer el valor de la cadena más de una vez, o si hay algunos agregados a la cadena, entonces debe ser un generador de cadenas. He visto aplicaciones que escribí en el pasado antes de aprender sobre los constructores de cuerdas que han tenido una gran huella de memoria que parece seguir creciendo y creciendo. Cambiar estos programas para usar el generador de cadenas reduce significativamente el uso de la memoria. Ahora lo juro por el constructor de cuerdas.
Mi enfoque siempre ha sido utilizar StringBuilder al concatenar 4 o más cadenas OR Cuando no sé cómo pueden tener lugar las concatenaciones.
StringBuilder es probablemente preferible. El motivo es que asigna más espacio del que se necesita actualmente (se establece el número de caracteres) para dejar espacio para futuros anexos. Entonces esos apéndices futuros que se ajustan al búfer actual no requieren ninguna asignación de memoria o recolección de basura, lo que puede ser costoso. En general, uso StringBuilder para la concatentación de cadenas complejas o el formato múltiple, luego me convierto en una cadena normal cuando los datos están completos, y quiero un objeto inmutable nuevamente.
Si estás haciendo mucha concatenación de cadenas, usa un StringBuilder. Cuando concatena con una Cadena, crea una Cadena nueva cada vez, usando más memoria.
Alex