String vs. StringBuilder

215

Entiendo la diferencia entre Stringy StringBuilder( StringBuilderser mutable) pero ¿hay una gran diferencia de rendimiento entre los dos?

El programa en el que estoy trabajando tiene muchos anexos de cadena de mayúsculas y minúsculas (más de 500). ¿Está usando StringBuilderuna mejor opción?

Kuvo
fuente

Respuestas:

236

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 tratado de codificar por 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 los dos, ahora lo pienso un poco más cuidadosamente.

Afortunadamente, es relativamente sencillo ejecutar un análisis de rendimiento en su código para ver dónde pasa el tiempo y luego modificarlo para usarlo StringBuilderdonde sea necesario.

Jay Bazuzi
fuente
44
Una buena regla general es usar Strings cuando no va a cambiarlo y usar StringBuilder si lo va a cambiar.
Tim
31
Me gusta mucho esta respuesta, especialmente el consejo de codificar para mayor claridad antes del rendimiento. Como desarrolladores, pasamos tanto o más tiempo leyendo el código como lo escribimos.
Scott Lawrence
Fuera de la ley: creo que debería convertirse en su propia respuesta que se vota por separado, si entiendo StackOverflow correctamente.
Jay Bazuzi
2
Sobre la base de mi experiencia, si intentas concatenar alrededor de 10-15 cuerdas, puedes ir con una cuerda, pero si no. de cadenas son más que eso, entonces use el generador de cadenas
Peeyush
3
Todavía depende de cómo se use, para una referencia a las pruebas que realmente me gustan Coding Horror - The Sad Tragedy of Micro-Optimization Theater
Erik Philips
56

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), usa internamente StringBuilder automáticamente (en realidad, usa una primitiva que StringBuilder también usa)

Sin embargo, si lo que está haciendo está más cerca de:

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

Luego debe 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ía usar el mismo StringBuilder hasta que termine de construir:

string a,b,c,d;
StringBuilder e = new StringBuilder();
 e.Append(b);
 e.Append(c);
 e.Append(d);
 a = e.ToString();
James Curran
fuente
55
No hay ninguna razón por la cual el compilador de C # necesite tratar la segunda muestra de manera diferente a la primera. En particular, no hay obligación de producir una cadena al final de cada línea. El compilador puede comportarse como usted dice, pero no tiene la obligación de hacerlo.
CodesInChaos
@CodesInChaos, se asigna específicamente una variable de cadena inmutable al final de cada línea, ¿no crea eso la obligación de producir una cadena? Sin embargo, estoy de acuerdo con el punto de que no hay razón para tratar cada línea individual de manera diferente (y no estoy seguro si lo hace), sin embargo, la pérdida de rendimiento proviene de la reasignación, por lo que no importaría.
Saeb Amini
@SaebAmini: si se atrata de una variable local y el objeto al que hace referencia no se ha asignado a otra variable (que podría ser accesible a otro subproceso), un buen optimizador podría determinar que ningún otro códigoa tiene acceso durante esta secuencia de líneas; solo el valor final de los asuntos. Por lo tanto, podría tratar esas tres líneas de código como si estuvieran escritas . aa = b + c + d;
ToolmakerSteve
29

StringBuilder es preferible SI está haciendo múltiples bucles o bifurcaciones en su pase de código ... sin embargo, para un rendimiento PURO, si puede salirse con una declaración 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 eficaz que la declaración de cadena única.

9 de cada 10 veces ... sin embargo, use el generador de cadenas.

En una nota al margen: string + var también es más eficiente que el string.

Calebjenkins
fuente
17
Desearía que hubieras dicho cómo sabes eso / cómo verificar eso.
ChrisW
2
No verifica el rendimiento en el reflector: verifica el rendimiento cronometrando el código de liberación, analiza con un generador de perfiles y busca una explicación con el reflector.
Albin Sunnanbo
3
Considere usar String.Format () para concatenar un pequeño número de cadenas pequeñas, especialmente si el objetivo es formatear mensajes para mostrar al usuario.
Gary Kindel
1
Esta es una información muy mala. ("string myString es más eficiente") no es cierto en absoluto.
Tom Stickel
3
La información en esta respuesta es incorrecta. StringBuilder es un poco más rápido que la concatenación realizada en la misma instrucción; sin embargo, solo notará una diferencia entre los dos si lo hace cientos de miles de veces ( Fuente ). Como se dijo en la fuente, "¡simplemente no importa!"
David Sherret
25

Un ejemplo simple para demostrar la diferencia de velocidad cuando se usa la Stringconcatenació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:

Usando StringBuilder: 10 milisegundos

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

Me parece que el uso StringBuilderes más rápido, mucho más rápido.

Diizzy
fuente
24

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 solemos escuchar ( todas las cadenas en .NET son inmutables ). Funciona manteniendo un búfer interno, una matriz de caracteres. 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 nueva matriz más grande y copia el búfer allí. Entonces, en el ejemplo anterior, StringBuilder solo podría necesitar una matriz para contener las 5 adiciones a la cadena, dependiendo del tamaño de su búfer. Puede decirle a StringBuilder qué tan grande debe ser su búfer en el constructor.

Matt Trunnell
fuente
2
Minor nit: es un poco extraño decir "creamos 13 objetos de cadena, y 12 de ellos fueron inútiles", y luego decir que StringBuilder soluciona este problema. Después de todo, seis de las cadenas no tiene más remedio que crear; que son de i.ToString(). Entonces, con StringBuilder, todavía tiene que crear 6 + 1 cadenas; redujo la creación de 13 cuerdas a la creación de 7 cuerdas. Pero esa sigue siendo la forma incorrecta de verlo; La creación de las cadenas de seis números no es relevante. En pocas palabras: simplemente no deberías haber mencionado las seis cadenas creadas por i.ToString(); no son parte de la comparación de eficiencia.
ToolmakerSteve
12

Sí, StringBuilderofrece un mejor rendimiento al realizar operaciones repetidas en una cadena. Esto 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 Vs Stringbuilder

  • String

    1. bajo Systemespacio de nombres
    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. bajo System.Textespacio de nombres
    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 de desbordamiento de pila: ¿ Mutabilidad de la cadena cuando la cadena no cambia en C #? .

Shamseer K
fuente
12

String Vs String Builder:

Lo primero que debes saber es ¿En qué asamblea viven estas dos clases?

Entonces,

la cadena está presente en el Systemespacio de nombres.

y

StringBuilder está presente en el System.Textespacio de nombres.

Para la declaración de cadena :

Tienes que incluir el Systemespacio de nombres. algo como esto. Using System;

y

Para la declaración de StringBuilder :

Tienes que incluir el System.textespacio de nombres. algo como esto. Using System.text;

Ahora viene la pregunta real.

¿Cuál es la diferencia entre string y StringBuilder ?

La principal diferencia entre estos dos es que:

La cadena es inmutable.

y

StringBuilder es mutable.

Así que ahora discutamos la diferencia entre inmutable y mutable

Mutable: significa que se puede cambiar.

Inmutable: significa que no se puede cambiar.

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."
        }
    }
}

Entonces, en este caso, vamos a cambiar el mismo objeto 5 veces.

¡Entonces 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.

ingrese la descripción de la imagen aquí

Explicacion:

Cuando inicializamos esta variable "nombre" a "Rehan", es decir string name = "Rehan" esta variable se crea en la pila "nombre" y señala ese valor "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.

Entonces, stringes inmutable lo que significa que una vez que creamos el objeto en la memoria no podemos cambiarlos.

Entonces, cuando concatenamos la namevariable, el objeto anterior permanece allí en la memoria y se crea otro nuevo objeto de cadena ...

Entonces, de la figura anterior, tenemos cinco objetos, los cuatro objetos se tiran y no se usan en absoluto. Todavía permanecen en la memoria y ocupan la cantidad de memoria. El "recolector de basura" es el responsable de que los recursos de la memoria estén tan limpios.

Entonces, en el caso de una cadena en cualquier momento cuando manipulamos la cadena una y otra vez, tenemos muchos objetos creados y permanecen allí en la memoria.

Entonces esta es la historia de la variable de cadena.

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."
        }
    }
}

Entonces, en este caso, vamos a cambiar el mismo objeto 5 veces.

¡Entonces 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.

Miremos la figura. ingrese la descripción de la imagen aquí

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, todavía tendremos un solo objeto stringBuilder.

No tiene muchos objetos basura u objetos stringBuilder no referenciados porque se puede cambiar. ¿Es mutable, lo que significa que cambia con el tiempo?

Diferencias:

  • String está presente en el espacio de nombres del sistema, mientras que Stringbuilder está presente en el espacio de nombres System.Text.
  • La cadena es inmutable donde StringBuilder es mutabe.
Rehan Shah
fuente
8

StringBuilder reduce el número de asignaciones y asignaciones, a un costo de memoria adicional utilizada. Si se usa correctamente, puede eliminar por completo 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
Moswald
fuente
5

El rendimiento de una operación de concatenación para un objeto String o StringBuilder depende de la frecuencia con que se produce una asignación de memoria. Una operación de concatenación String 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 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, el compilador podría 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

usuario487069
fuente
3

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 varias 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 compiladores lo hacen "plegado constante", un proceso para reducir el árbol de análisis cuando tienes un montón de manipulación constante (también se usa cuando escribes algo así int minutesPerYear = 24 * 365 * 60). Y para casos simples con valores no constantes agregados entre sí, el compilador .NET reducirá su código a algo similar a lo queStringBuilder hace.

Pero cuando el compilador no puede reducir su apéndice a algo más simple, querrá un StringBuilder. Como señala fizch, es más probable que suceda dentro de un bucle.

JasonTrue
fuente
2

Creo que StringBuilder es más rápido si tiene más de 4 cadenas que necesita unir. Además, puede hacer algunas cosas interesantes como AppendLine.

Gilligan
fuente
2

En .NET, StringBuilder es aún más rápido que agregar cadenas. Estoy bastante seguro de que en Java, solo crean un StringBuffer debajo del capó cuando agregas cadenas, por lo que no hay realmente una diferencia. No estoy seguro de por qué aún no han hecho esto en .NET.

Eric Z Beard
fuente
2

El uso de cadenas para la concatenación puede conducir a una complejidad de tiempo de ejecución del orden de O(n^2).

Si usa a StringBuilder, hay mucho menos copia de memoria que debe hacerse. Con el StringBuilder(int capacity)puede aumentar el rendimiento si puede estimar qué tan grande Stringserá la final . Incluso si no es preciso, probablemente solo tendrá que aumentar la capacidad StringBuilderun par de veces, lo que también puede ayudar al rendimiento.

Steve g
fuente
2

He visto ganancias significativas de rendimiento al usar la EnsureCapacity(int capacity)llamada al método en una instancia de StringBuilderantes de usarla para cualquier almacenamiento de cadenas. Normalmente llamo a eso en la línea de código después de la creación de instancias. Tiene el mismo efecto que si crea una instancia StringBuildercomo esta:

var sb = new StringBuilder(int capacity);

Esta llamada asigna la memoria necesaria antes de tiempo, lo que provoca menos asignaciones de memoria durante varias Append()operaciones. Debe hacer una suposición educada sobre cuánta memoria necesitará, pero para la mayoría de las aplicaciones esto no debería ser demasiado difícil. Por lo general, me equivoco con demasiada memoria (estamos hablando de 1k o menos).

Jason Jackson
fuente
No hay necesidad de llamar EnsureCapacityjusto después de la StringBuilderinstanciación. Simplemente una instancia de la StringBuildersiguiente manera: var sb = new StringBuilder(int capacity).
David Ferenczy Rogožan
Me dijeron que ayuda usar un número primo.
Karl Gjertsen
1

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.

RichS
fuente
1

String y StringBuilder son en realidad ambos inmutables, StringBuilder ha incorporado buffers que permiten que su tamaño se administre de manera más eficiente. Cuando StringBuilder necesita cambiar el tamaño es cuando se reasigna 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);

capgpilk
fuente
2
No estoy seguro de entender lo que significa inmutable. Las cadenas son inmutables, NO SE PUEDEN cambiar. Cualquier cosa "cambiada" es en realidad solo que el valor anterior permanece en el montón sin CUALQUIER puntero para localizarlo. StringBuilder (mutable) es un tipo de referencia en el montón, se cambia el puntero al montón y la asignación de espacio para este cambio.
Tom Stickel
1

La concatenación de cadenas le costará más. En Java, puede usar StringBuffer o StringBuilder según sus necesidades. 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 sincronizada o segura para subprocesos, vaya a StringBuilder. Esto será más rápido que la concatenación de String y también más rápido que StringBuffer ya que no hay sobrecarga de sincronización.

raffimd
fuente
0

StringBuilder es probablemente preferible. La razón es que asigna más espacio del que se necesita actualmente (usted establece el número de caracteres) para dejar espacio para futuros anexos. Entonces, esos anexos futuros que encajan en el 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 concatenación de cadenas complejas o el formato múltiple, luego convierto a una cadena normal cuando los datos están completos, y quiero un objeto inmutable nuevamente.

deemer
fuente
0

Si está haciendo mucha concatenación de cadenas, use un StringBuilder. Cuando concatena con una cadena, crea una nueva cadena cada vez, utilizando más memoria.

Alex

Alex Fort
fuente
0

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 he escrito en el pasado antes de aprender sobre los constructores de cadenas que han tenido una gran huella de memoria que parece seguir creciendo y creciendo. Cambiar estos programas para usar el generador de cadenas redujo significativamente el uso de memoria. Ahora lo juro por el constructor de cuerdas.

usuario13288
fuente
0

StringBuilder es significativamente más eficiente, pero no verá ese rendimiento a menos que esté haciendo 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 iteraciones grandes.

Como puede ver, las 200,000 iteraciones tomaron 22 segundos, mientras que el millón de iteraciones que usó StringBuilderfue 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:

A partir de String + el 28/01/2013 16:55:40.

String + terminado el 28/01/2013 16:55:40.

A partir de String + el 28/01/2013 16:55:40.

String + terminado el 28/01/2013 16:56:02.

A partir de Sb anexar el 28/01/2013 16:56:02.

Sb terminado anexado el 28/01/2013 16:56:02.

CathalMF
fuente
1
Si el generador de cadenas es tan poderoso, ¿por qué tenemos cadena? ¿Por qué no borramos completamente String? Hice esta pregunta para que pueda decirme el BENEFICIO de String over StringBuilder
irrompible
-2

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

En desarrolloChris
fuente