La forma más eficiente de concatenar cadenas?

¿Cuál es la forma más eficiente de concatenar cadenas?

El método StringBuilder.Append() es mucho mejor que usar el operador +. Pero descubrí que, al ejecutar 1000 concatenaciones o menos, String.Join() es incluso más eficiente que StringBuilder .

 StringBuilder sb = new StringBuilder(); sb.Append(someString); 

El único problema con String.Join es que debe concatenar las cadenas con un delimitador común. (Editar 🙂 como @ryanversaw señaló, puedes hacer que el delimitador sea string.Empty.

 string key = String.Join("_", new String[] { "Customers_Contacts", customerID, database, SessionID }); 

Rico Mariani , el gurú de rendimiento de .NET, tenía un artículo sobre este mismo tema. No es tan simple como uno podría sospechar. El consejo básico es este:

Si su patrón se ve así:

x = f1(...) + f2(...) + f3(...) + f4(...)

ese es un concat y es zippy, StringBuilder probablemente no ayude.

Si su patrón se ve así:

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

entonces probablemente quieras StringBuilder.

Otro artículo más para apoyar esta afirmación proviene de Eric Lippert, donde describe las optimizaciones realizadas en una línea + concatenaciones de una manera detallada.

Hay 6 tipos de concatenaciones de cadenas:

  1. Usando el símbolo más ( + ).
  2. Usando string.Concat() .
  3. Usando string.Join() .
  4. Usando string.Format() .
  5. Usando string.Append() .
  6. Usando StringBuilder .

En un experimento, se ha demostrado que string.Concat() es la mejor forma de acercarse si las palabras son menores de 1000 (aproximadamente) y si las palabras son más de 1000, se debe utilizar StringBuilder .

Para obtener más información, consulte este sitio .

string.Join () versus string.Concat ()

El método string.Concat aquí es equivalente a la invocación del método string.Join con un separador vacío. Agregar una cadena vacía es rápido, pero no hacerlo es aún más rápido, por lo que el método string.Concat sería superior aquí.

Desde Chinh Do: StringBuilder no siempre es más rápido :

Reglas de juego

  • Al concatenar tres valores de cadenas dinámicas o menos, use la concatenación de cadenas tradicional.

  • Al concatenar más de tres valores de cadena dynamics, use StringBuilder.

  • Al construir una cadena grande a partir de varios literales de cadena, use el @ literal de cadena o el operador de línea +.

La mayoría de las veces StringBuilder es su mejor opción, pero hay casos como se muestra en esa publicación que al menos deberían pensar en cada situación.

Si está operando en un bucle, StringBuilder es probablemente el camino a seguir; te ahorra la sobrecarga de crear nuevas cadenas de forma regular. Sin embargo, en el código que solo se ejecutará una vez, String.Concat probablemente esté bien.

Sin embargo, Rico Mariani (gurú de la optimización de .NET) formó una prueba en la que afirmó al final que, en la mayoría de los casos, recomienda String.Format.

De este artículo de MSDN :

Hay algunos gastos generales asociados con la creación de un objeto StringBuilder, tanto en tiempo como en memoria. En una máquina con memoria rápida, un StringBuilder vale la pena si estás haciendo unas cinco operaciones. Como regla general, diría que 10 o más operaciones de cuerda son una justificación para la sobrecarga en cualquier máquina, incluso una más lenta.

Entonces, si confías en MSDN, ve con StringBuilder si tienes que hacer más de 10 cadenas de operaciones / concatenaciones; de lo contrario, la cadena simple concat con ‘+’ está bien.

Este es el método más rápido que he desarrollado durante una década para mi aplicación NLP a gran escala. Tengo variaciones para IEnumerable y otros tipos de entrada, con y sin separadores de diferentes tipos ( Char , String ), pero aquí muestro el caso simple de concatenar todas las cadenas de una matriz en una sola cadena, sin separador. La última versión aquí está desarrollada y probada en unidades en C # 7 y .NET 4.7 .

Hay dos claves para un mayor rendimiento; el primero es calcular previamente el tamaño total exacto requerido. Este paso es trivial cuando la entrada es una matriz como se muestra aquí. Para manejar IEnumerable lugar, vale la pena reunir las cadenas en una matriz temporal para calcular ese total (la matriz es necesaria para evitar llamar a ToString() más de una vez por elemento ya que técnicamente, dada la posibilidad de efectos secundarios, hacerlo podría cambiar la semántica esperada de una operación de ‘unión de cadena’).

A continuación, dado el tamaño de asignación total de la cadena final, se obtiene el mayor impulso en el rendimiento construyendo la cadena de resultados en el lugar . Hacer esto requiere la técnica (quizás controvertida) de suspender temporalmente la inmutabilidad de una nueva String que inicialmente se asigna llena de ceros. Cualquier controversia de ese lado aparte, sin embargo …

… tenga en cuenta que esta es la única solución de concatenación masiva en esta página que evita completamente una ronda adicional de asignación y copia por parte del constructor de String .

Código completo:

 ///  /// Concatenate the strings in 'rg', none of which may be null, into a single String. ///  public static unsafe String StringJoin(this String[] rg) { int i; if (rg == null || (i = rg.Length) == 0) return String.Empty; if (i == 1) return rg[0]; String s, t; int cch = 0; do cch += rg[--i].Length; while (i > 0); if (cch == 0) return String.Empty; i = rg.Length; fixed (Char* _p = (s = new String(default(Char), cch))) { Char* pDst = _p + cch; do if ((t = rg[--i]).Length > 0) fixed (Char* pSrc = t) memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1)); while (pDst > _p); } return s; } [DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)] static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb); 

Debo mencionar que este código tiene una ligera modificación de lo que yo uso. En el original, llamo a la instrucción cpblk IL desde C # para hacer la copia real. Por simplicidad y portabilidad en el código aquí, lo reemplacé con P / Invoke memcpy en memcpy lugar, como puede ver. Para obtener el mayor rendimiento en x64 ( pero tal vez no en x86 ), puede utilizar el método cpblk .

Agregando a las otras respuestas, tenga en cuenta que a StringBuilder se le puede decir una cantidad inicial de memoria para asignar .

El parámetro de capacidad define la cantidad máxima de caracteres que se pueden almacenar en la memoria asignada por la instancia actual. Su valor se asigna a la propiedad Capacidad . Si el número de caracteres que se almacenará en la instancia actual excede este valor de capacidad , el objeto StringBuilder asigna memoria adicional para almacenarlos.

Si la capacidad es cero, se utiliza la capacidad predeterminada específica de la implementación.

Agregar repetidamente un StringBuilder que no se ha asignado previamente puede generar muchas asignaciones innecesarias, como concatenar repetidamente cadenas regulares.

Si sabe cuánto tiempo durará la cadena final, puede calcularla trivialmente o puede hacer una conjetura sobre el caso común (asignar demasiado no es necesariamente algo malo), debe proporcionar esta información al constructor o al Propiedad de capacidad . Especialmente cuando se ejecutan pruebas de rendimiento para comparar StringBuilder con otros métodos como String.Concat, que hacen lo mismo internamente. Cualquier prueba que vea en línea que no incluya la preasignación de StringBuilder en sus comparaciones es incorrecta.

Si no puede adivinar el tamaño, probablemente esté escribiendo una función de utilidad que debería tener su propio argumento opcional para controlar la preasignación.

También es importante señalar que debe usar el operador + si está concatenando literales de cadena .

Cuando concatena literales de cadena o constantes de cadena utilizando el operador +, el comstackdor crea una sola cadena. No se produce la concatenación de tiempo de ejecución.

Cómo: Concatenar cadenas múltiples (Guía de progtwigción C #)

Lo siguiente puede ser una solución alternativa más para concatenar múltiples cadenas.

 String str1 = "sometext"; string str2 = "some other text"; string afterConcate = $"{str1}{str2}"; 

cadena de interpolación

Lo más eficiente es usar StringBuilder, así:

 StringBuilder sb = new StringBuilder(); sb.Append("string1"); sb.Append("string2"); ...etc... String strResult = sb.ToString(); 

@jonezy: String.Concat está bien si tienes un par de cosas pequeñas. Pero si está concatenando megabytes de datos, es probable que su progtwig sea un tanque.

Realmente depende de tu patrón de uso. Un punto de referencia detallado entre string.Join, string, Concat y string.Format se puede encontrar aquí: String.Format no es adecuado para el registro intensivo

(Esta es en realidad la misma respuesta que di a esta pregunta)

System.String es inmutable. Cuando modificamos el valor de una variable de cadena, se asigna una nueva memoria al nuevo valor y se libera la asignación de memoria anterior. System.StringBuilder fue diseñado para tener el concepto de una cadena mutable donde se pueden realizar una variedad de operaciones sin asignar ubicación de memoria separada para la cadena modificada.

Prueba estos 2 pedazos de código y encontrarás la solución.

  static void Main(string[] args) { StringBuilder s = new StringBuilder(); for (int i = 0; i < 10000000; i++) { s.Append( i.ToString()); } Console.Write("End"); Console.Read(); } 

Vs

 static void Main(string[] args) { string s = ""; for (int i = 0; i < 10000000; i++) { s += i.ToString(); } Console.Write("End"); Console.Read(); } 

Descubrirá que el 1er código terminará realmente rápido y la memoria será buena.

El segundo código tal vez la memoria estará bien, pero tomará más tiempo ... mucho más. Entonces, si tiene una aplicación para muchos usuarios y necesita velocidad, use la 1ra. Si tienes una aplicación para una aplicación de usuario de corto plazo, tal vez puedas usar ambas o la segunda será más "natural" para los desarrolladores.

Aclamaciones.

Para solo dos cadenas, definitivamente no desea usar StringBuilder. Existe un umbral por encima del cual la sobrecarga de StringBuilder es menor que la sobrecarga de asignar múltiples cadenas.

Entonces, para más de 2-3 cuerdas, use el código de DannySmurf . De lo contrario, simplemente use el operador +.

Dependería del código. StringBuilder es más eficiente en general, pero si solo concatena unas pocas cadenas y lo hace todo en una sola línea, las optimizaciones de código probablemente se encarguen de ello. Es importante pensar en cómo se ve el código: para conjuntos más grandes, StringBuilder facilitará la lectura, para los pequeños, StringBuilder simplemente agregará desorden innecesario.

Otra solución:

dentro del ciclo, use List en lugar de string.

 List lst= new List(); for(int i=0; i<100000; i++){ ........... lst.Add(...); } return String.Join("", lst.ToArray());; 

es muy muy rápido