String vs. StringBuilder

Entiendo la diferencia entre String y StringBuilder ( StringBuilder es mutable) pero ¿hay una gran diferencia de rendimiento entre los dos?

El progtwig en el que estoy trabajando tiene una gran cantidad de cadenas de casos (más de 500). ¿Está utilizando StringBuilder una mejor opción?

Sí, la diferencia de rendimiento es significativa. Consulte el artículo de KB ” Cómo mejorar el rendimiento de concatenación de cadenas en Visual C # “.

Siempre he intentado codificar primero para claridad y luego optimizar para el rendimiento más adelante. ¡Eso es mucho más fácil que hacerlo al revés! Sin embargo, después de haber visto la enorme diferencia de rendimiento en mis aplicaciones entre los dos, ahora pienso un poco más cuidadosamente.

Afortunadamente, es relativamente sencillo ejecutar análisis de rendimiento en su código para ver dónde está gastando el tiempo y luego modificarlo para usar StringBuilder cuando sea necesario.

Para aclarar lo que dijo Gillian sobre 4 cuerdas, si tiene algo como esto:

 string a,b,c,d; a = b + c + d; 

entonces sería más rápido usando cadenas y el operador más. Esto se debe a que (como Java, como señala Eric), internamente usa StringBuilder automáticamente (en realidad, usa una primitiva que StringBuilder también usa)

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” tiene que ser una cadena (inmutable), por lo que debería crear y disponer un StringBuilder en cada línea. Para la velocidad, necesitarías usar el mismo StringBuilder hasta que termines 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 preferible SI está haciendo múltiples bucles, o se bifurca en su paso de código … sin embargo, para un rendimiento PURO, si puede salirse con la suya con una statement de cadena SINGLE , entonces eso es mucho más eficiente.

Por ejemplo:

 string myString = "Some stuff" + var1 + " more stuff" + var2 + " other stuff" .... etc... etc...; 

es más eficiente 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 fácil de mantener, pero no es más eficiente que la statement de cadena única.

Sin embargo, 9 de cada 10 veces … usa el generador de cadenas.

En una nota al margen: string + var también es más eficiente que la cadena. Enfoque de formato (generalmente) que utiliza un StringBuilder internamente (cuando hay 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 corrige este problema. No es una "cadena mutable" como a menudo escuchamos ( todas las cadenas en .NET son inmutables ). Funciona al mantener un buffer interno, una matriz de char. Llamar a Append () o AppendLine () agrega la cadena al espacio vacío al final de la matriz de caracteres; si la matriz es demasiado pequeña, crea una matriz nueva y más grande, y copia el búfer allí. Entonces, en el ejemplo anterior, StringBuilder solo podría necesitar una sola matriz para contener las 5 adiciones a la cadena, dependiendo del tamaño de su buffer. Puede decirle a StringBuilder qué tan grande debe ser su buffer en el constructor.

Un ejemplo simple para demostrar la diferencia de velocidad cuando se usa String contactación de String frente a 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 contactenation: " + time.ElapsedMilliseconds + " milliseconds"); 

Resultado:

Uso de la contactació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:

Usando StringBuilder: 10 milisegundos

Como resultado, la primera iteración tomó 15423 ms mientras que la segunda iteración usando StringBuilder tomó 10 ms.

Me parece que usar StringBuilder es más rápido, mucho más rápido.

No vayas ciegamente por StringBuilder. Hay escenarios en los que StringBuilder no mejora el rendimiento.

Rico Mariani ha escrito dos interesantes entradas de blog sobre esto:

http://blogs.msdn.com/ricom/archive/2003/12/02/40778.aspx

http://blogs.msdn.com/ricom/archive/2003/12/15/43628.aspx

Sí, StringBuilder ofrece un mejor rendimiento al realizar operaciones repetidas sobre una cadena. Es porque todos los cambios se realizan en una sola instancia, por lo que puede ahorrar mucho tiempo en lugar de crear una instancia nueva como String .

String Vs Stringbuilder

  • String

    1. en el espacio de nombres del System
    2. instancia inmutable (solo lectura)
    3. el rendimiento se degrada cuando se produce un cambio continuo de valor
    4. a salvo de amenazas
  • StringBuilder (cadena mutable)

    1. en el System.Text nombres System.Text
    2. instancia mutable
    3. muestra un mejor rendimiento ya que se realizan nuevos cambios en la instancia existente

Recomiendo encarecidamente el artículo de dotnet mob: String Vs StringBuilder en C # .

Pregunta relacionada con desbordamiento de stack: ¿ capacidad de mutación de la cadena cuando la cadena no cambia en C #? .

StringBuilder reduce el número de asignaciones y asignaciones, a un costo adicional de memoria utilizada. Utilizado correctamente, puede eliminar por completo la necesidad de que el comstackdor distribuya cadenas cada vez más grandes 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 

La ejecución 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 StringBuilder solo asigna memoria si el búfer de objetos StringBuilder es demasiado pequeño para acomodar los datos nuevos. 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 operaciones de concatenación individuales incluso podrían combinarse en una única operación por el comstackdor. Un objeto StringBuilder es preferible para una operación de concatenación si se concatenan un número arbitrario de cadenas; por ejemplo, si un bucle concatena un número aleatorio de cadenas de entrada del usuario.

Fuente: MSDN

StringBuilder es mejor para construir una cadena a partir de muchos valores no constantes.

Si está creando una cadena a partir de una gran cantidad de valores constantes, como múltiples líneas de valores en un documento HTML o XML u otros fragmentos de texto, puede salirse con solo agregar a la misma cadena, porque casi todos los comstackdores hacen “Plegado constante”, un proceso de reducción del árbol de análisis sintáctico cuando se tiene un montón de manipulación constante (también se usa cuando se escribe algo como int minutesPerYear = 24 * 365 * 60 ). Y para casos simples con valores no constantes adjuntos, el comstackdor .NET reducirá su código a algo similar a lo que hace StringBuilder .

Pero cuando tu apéndice no puede ser reducido a algo más simple por el comstackdor, querrás un StringBuilder . Como señala Fizch, es más probable que ocurra dentro de un ciclo.

Creo que StringBuilder es más rápido si tienes más de 4 cadenas que debes agregar juntas. Además, puede hacer algunas cosas interesantes como AppendLine.

En .NET, StringBuilder es aún más rápido que la adición de cadenas. Estoy bastante seguro de que en Java, simplemente crean un StringBuffer bajo el capó cuando añades cadenas, por lo que no hay realmente una diferencia. No estoy seguro de por qué aún no han hecho esto en .NET.

Considere la ” Triste tragedia 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 un StringBuilder , hay que hacer mucha menos copia de la memoria. Con StringBuilder(int capacity) puede boost el rendimiento si puede estimar qué tan grande será la String final. Incluso si no es preciso, probablemente solo tendrá que boost la capacidad de StringBuilder un par de veces, lo que también puede ayudar a mejorar el rendimiento.

He visto mejoras significativas en el rendimiento al utilizar la llamada al método EnsureCapacity(int capacity) en una instancia de StringBuilder antes de usarlo para cualquier almacenamiento de cadena. Normalmente lo llamo en la línea de código después de la instanciación. Tiene el mismo efecto que si instancias el StringBuilder siguiente manera:

 var sb = new StringBuilder(int capacity); 

Esta llamada asigna memoria necesaria con anticipación, lo que ocasiona menos asignaciones de memoria durante varias operaciones de Append() . Tienes que adivinar cuidadosamente cuánta memoria necesitarás, pero para la mayoría de las aplicaciones esto no debería ser demasiado difícil. Por lo general, me equivoco por el exceso de memoria (estamos hablando de más o menos 1k).

Además de las respuestas anteriores, lo primero que siempre hago cuando pienso en problemas 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 cadenas de entradas definitivamente debería utilizar StringBuilder.

String y StringBuilder son realmente inmutables, StringBuilder tiene búferes integrados que permiten administrar su tamaño de manera más eficiente. Cuando StringBuilder necesita cambiar el tamaño es cuando se vuelve a asignar en el montón. Por defecto tiene un tamaño de 16 caracteres, puede configurar esto en el constructor.

p.ej.

StringBuilder sb = new StringBuilder (50);

La concatenación de cadenas te costará más. En Java, puede usar StringBuffer o StringBuilder en función de sus necesidades. Si desea una implementación sincronizada y segura para hilos, vaya a StringBuffer. Esto será más rápido que la concatenación de cadenas.

Si no necesita implementación sincronizada o Thread safe, 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 una sobrecarga de sincronización.

StringBuilder es probablemente preferible. La razón 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, los anexos futuros que quepan en el búfer actual no requieren ninguna asignación de memoria o recolección de basura, lo que puede ser costoso. En general, utilizo StringBuilder para la concatenación de cadenas complejas o el formateo múltiple, luego convierto a una cadena normal cuando los datos están completos, y quiero un objeto inmutable de nuevo.

Si está haciendo una gran cantidad de concatenación de cadenas, use un StringBuilder. Cuando concatenas con una Cadena, creas una nueva Cadena cada vez, usando más memoria.

Alex

Como regla general, si tengo que establecer el valor de la cadena más de una vez, o si hay alguna que se agregue a la cadena, entonces debe ser un generador de cadenas. He visto aplicaciones que he escrito en el pasado antes de aprender sobre constructores de cuerdas que han tenido una gran huella de memoria que parece seguir creciendo y creciendo. Cambiar estos progtwigs para usar el generador de cadenas reduce significativamente el uso de la memoria. Ahora juro por el generador de cuerdas.

Mi enfoque siempre ha sido utilizar StringBuilder al concatenar 4 o más cadenas O BIEN Cuando no sé cómo pueden tener lugar las concatenaciones.

Buen artículo relacionado con el rendimiento aquí

StringBuilder es significativamente más eficiente, pero no verá ese rendimiento a menos que esté haciendo una gran cantidad de modificaciones de cadena.

A continuación se muestra un fragmento rápido de código para dar un ejemplo del rendimiento. Como puede ver, realmente solo comienza a ver un gran aumento en el rendimiento cuando ingresa en grandes iteraciones.

Como puede ver, las 200,000 iteraciones tomaron 22 segundos, mientras que el 1 millón de iteraciones usando StringBuilder fue casi instantáneo.

 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:

Beginning String + al 28/01/2013 16:55:40.

Terminó Cadena + al 28/01/2013 16:55:40.

Beginning String + al 28/01/2013 16:55:40.

Terminó Cadena + al 28/01/2013 16:56:02.

A partir Sb apéndice en 28/01/2013 16:56:02.

Sb finalizado anexar al 28/01/2013 16:56:02.

StringBuilder tendrá un mejor rendimiento, desde un punto de soporte de memoria. En cuanto al procesamiento, la diferencia en el tiempo de ejecución puede ser insignificante.