Entiendo los beneficios de StringBuilder.
Pero si quiero concatenar 2 cadenas, supongo que es mejor (más rápido) hacerlo sin StringBuilder. ¿Es esto correcto?
¿En qué punto (número de cadenas) es mejor utilizar StringBuilder?
Entiendo los beneficios de StringBuilder.
Pero si quiero concatenar 2 cadenas, supongo que es mejor (más rápido) hacerlo sin StringBuilder. ¿Es esto correcto?
¿En qué punto (número de cadenas) es mejor utilizar StringBuilder?
Respuestas:
Le sugiero encarecidamente que lea La triste tragedia del teatro de microoptimización , de Jeff Atwood.
Trata la concatenación simple frente a StringBuilder frente a otros métodos.
Ahora, si quieres ver algunos números y gráficos, sigue el enlace;)
Pero si quiero concatinar 2 cadenas, supongo que es mejor (más rápido) hacerlo sin StringBuilder. ¿Es esto correcto?
Eso es correcto, puede encontrar el por qué exactamente explicado muy bien en:
http://www.yoda.arachsys.com/csharp/stringbuilder.html
En resumen: si puedes concatinar cadenas de una sola vez, como
var result = a + " " + b + " " + c + ..
está mejor sin StringBuilder porque solo se realiza una copia (la longitud de la cadena resultante se calcula de antemano);
Para estructuras como
var result = a;
result += " ";
result += b;
result += " ";
result += c;
..
se crean nuevos objetos cada vez, por lo que debería considerar StringBuilder.
Al final, el artículo resume estas reglas generales:
Reglas de juego
Entonces, ¿cuándo debería usar StringBuilder y cuándo debería usar los operadores de concatenación de cadenas?
Definitivamente use StringBuilder cuando esté concatenando en un bucle no trivial, especialmente si no sabe con certeza (en el momento de la compilación) cuántas iteraciones realizará a través del bucle. Por ejemplo, leer un archivo de un carácter a la vez, construir una cadena sobre la marcha usando el operador + = es potencialmente un suicidio de rendimiento.
Definitivamente use el operador de concatenación cuando pueda (de manera legible) especificar todo lo que debe concatenarse en una declaración. (Si tiene una variedad de cosas para concatenar, considere llamar a String.Concat explícitamente, o String.Join si necesita un delimitador).
No tenga miedo de dividir los literales en varios bits concatenados; el resultado será el mismo. Puede mejorar la legibilidad dividiendo un literal largo en varias líneas, por ejemplo, sin dañar el rendimiento.
Si necesita los resultados intermedios de la concatenación para algo más que alimentar la siguiente iteración de concatenación, StringBuilder no lo ayudará. Por ejemplo, si crea un nombre completo a partir de un nombre y un apellido, y luego agrega una tercera información (el apodo, tal vez) al final, solo se beneficiará de usar StringBuilder si no lo hace necesita la cadena (nombre + apellido) para otro propósito (como lo hacemos en el ejemplo que crea un objeto Persona).
Si solo tiene que hacer algunas concatenaciones y realmente quiere hacerlas en declaraciones separadas, no importa en qué dirección vaya. La forma más eficiente dependerá del número de concatenaciones, los tamaños de cadena involucrados y el orden en el que se concatenan. Si realmente cree que ese fragmento de código es un cuello de botella en el rendimiento, perfílelo o evalúelo en ambos sentidos.
System.String es un objeto inmutable; significa que cada vez que modifiques su contenido, asignará una nueva cadena y esto lleva tiempo (¿y memoria?). Con StringBuilder, modifica el contenido real del objeto sin asignar uno nuevo.
Por lo tanto, use StringBuilder cuando necesite hacer muchas modificaciones en la cadena.
Realmente no ... deberías usar StringBuilder si concatenas cadenas grandes o tienes muchas concatenaciones, como en un bucle.
StringBuilder
solo si el bucle o la concatenación es un problema de rendimiento para las especificaciones.
string s = "abcd"
, al menos eso es lo último Escuché ... aunque, con variables sería, muy probablemente, Concat.
a + "hello" + "somethingelse"
y nunca tuve que preocuparme por eso. Si se convierte en un problema, usaré StringBuilder. Pero no me preocupé por ello en primer lugar y pasé menos tiempo escribiéndolo.
Aquí hay una aplicación de prueba simple para demostrar el punto:
class Program
{
static void Main(string[] args)
{
const int testLength = 30000;
var StartTime = DateTime.Now;
//TEST 1 - String
StartTime = DateTime.Now;
String tString = "test string";
for (int i = 0; i < testLength; i++)
{
tString += i.ToString();
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 2000 ms
//TEST 2 - StringBuilder
StartTime = DateTime.Now;
StringBuilder tSB = new StringBuilder("test string");
for (int i = 0; i < testLength; i++)
{
tSB.Append(i.ToString());
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 4 ms
Console.ReadLine();
}
}
Resultados:
30.000 iteraciones
1000 iteraciones
500 iteraciones
Parafrasear
Entonces contarás hasta tres, ni más ni menos. Tres será el número que contarás, y el número del conteo será tres. No contarás cuatro, ni contarás dos, excepto que luego pasarás a tres. Una vez que se alcanza el número tres, siendo el tercer número, entonces lanza su Granada de Mano Santa de Antioquía
Generalmente uso el generador de cadenas para cualquier bloque de código que resulte en la concatenación de tres o más cadenas.
No hay una respuesta definitiva, solo reglas generales. Mis propias reglas personales son algo como esto:
StringBuilder
.StringBuilder
.StringBuilder
.Pero si quiero concatenar 2 cadenas, supongo que es mejor y más rápido hacerlo sin StringBuilder. ¿Es esto correcto?
Si. Pero lo que es más importante, es mucho más legible usar una vainilla String
en tales situaciones. Usarlo en un bucle, por otro lado, tiene sentido y también puede ser tan legible como la concatenación.
Sería cauteloso con las reglas generales que citan números específicos de concatenación como umbral. Usarlo en bucles (y solo en bucles) es probablemente igual de útil, más fácil de recordar y tiene más sentido.
Siempre que pueda escribir físicamente el número de concatenaciones (a + b + c ...) no debería haber una gran diferencia. N al cuadrado (en N = 10) es una desaceleración de 100 veces, lo que no debería ser tan malo.
El gran problema es cuando está concatenando cientos de cadenas. A N = 100, obtienes una ralentización de 10000 veces. Lo cual es bastante malo.
Dado que es difícil encontrar una explicación para esto que no esté influenciado por opiniones o seguido por una batalla de orgullo, pensé en escribir un poco de código en LINQpad para probarlo yo mismo.
Descubrí que usar cadenas de tamaño pequeño en lugar de usar i.ToString () cambia los tiempos de respuesta (visibles en pequeños bucles).
La prueba utiliza diferentes secuencias de iteraciones para mantener las mediciones de tiempo en rangos sensiblemente comparables.
Copiaré el código al final para que pueda probarlo usted mismo (results.Charts ... Dump () no funcionará fuera de LINQPad).
Salida (eje X: número de iteraciones probadas, eje Y: tiempo empleado en tics):
Secuencia de iteraciones: 2, 3, 4, 5, 6, 7, 8, 9, 10
Secuencia de iteraciones: 10, 20, 30, 40, 50, 60, 70, 80
Secuencia de iteraciones: 100, 200, 300, 400, 500
Código (escrito con LINQPad 5):
void Main()
{
Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
Test(10, 20, 30, 40, 50, 60, 70, 80);
Test(100, 200, 300, 400, 500);
}
void Test(params int[] iterationsCounts)
{
$"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();
int testStringLength = 10;
RandomStringGenerator.Setup(testStringLength);
var sw = new System.Diagnostics.Stopwatch();
var results = new Dictionary<int, TimeSpan[]>();
// This call before starting to measure time removes initial overhead from first measurement
RandomStringGenerator.GetRandomString();
foreach (var iterationsCount in iterationsCounts)
{
TimeSpan elapsedForString, elapsedForSb;
// string
sw.Restart();
var str = string.Empty;
for (int i = 0; i < iterationsCount; i++)
{
str += RandomStringGenerator.GetRandomString();
}
sw.Stop();
elapsedForString = sw.Elapsed;
// string builder
sw.Restart();
var sb = new StringBuilder(string.Empty);
for (int i = 0; i < iterationsCount; i++)
{
sb.Append(RandomStringGenerator.GetRandomString());
}
sw.Stop();
elapsedForSb = sw.Elapsed;
results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
}
// Results
results.Chart(r => r.Key)
.AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
.AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
.DumpInline();
}
static class RandomStringGenerator
{
static Random r;
static string[] strings;
public static void Setup(int testStringLength)
{
r = new Random(DateTime.Now.Millisecond);
strings = new string[10];
for (int i = 0; i < strings.Length; i++)
{
strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
}
}
public static string GetRandomString()
{
var indx = r.Next(0, strings.Length);
return strings[indx];
}
}
No creo que haya una línea muy fina entre cuándo usar y cuándo no. A menos que, por supuesto, alguien haya realizado pruebas exhaustivas para obtener las condiciones de oro.
Para mí, no usaré StringBuilder si solo concateno 2 cadenas enormes. Si hay un bucle con un recuento indeterminista, es probable que lo haga, incluso si el bucle puede ser un recuento pequeño.
Una sola concatenación no vale la pena usar StringBuilder. Por lo general, he usado 5 concatenaciones como regla general.