¿Cómo funciona StringBuilder?

¿Cómo funciona StringBuilder ?

¿Qué hace internamente ? ¿Utiliza código inseguro? ¿Y por qué es tan rápido (en comparación con el operador + )?

Cuando usa el operador + para construir una cadena:

 string s = "01"; s += "02"; s += "03"; s += "04"; 

luego en la primera concatenación hacemos una nueva cadena de longitud cuatro y copiamos “01” y “02” en ella – se copian cuatro caracteres. En la segunda concatenación hacemos una nueva cadena de longitud seis y copiamos “0102” y “03” en ella – se copian seis caracteres. En el tercer concat, hacemos una cadena de longitud ocho y copiamos “010203” y “04” en ella – se copian ocho caracteres. Hasta el momento, se han copiado un total de 4 + 6 + 8 = 18 caracteres para esta cadena de ocho caracteres. Sigue adelante.

 ... s += "99"; 

En el 98th concat, hacemos una cadena de longitud 198 y copiamos “010203 … 98” y “99” en ella. Eso nos da un total de 4 + 6 + 8 + … + 198 = mucho, para hacer esta cadena de 198 caracteres.

Un generador de cadenas no hace toda esa copia. Por el contrario, mantiene una matriz mutable que se espera que sea más grande que la cadena final, y agrega cosas nuevas a la matriz según sea necesario.

¿Qué sucede cuando la suposición es incorrecta y la matriz se llena? Hay dos estrategias. En la versión anterior del marco, el generador de cadenas reasignó y copió la matriz cuando se llenó, y duplicó su tamaño. En la nueva implementación, el generador de cadenas mantiene una lista vinculada de matrices relativamente pequeñas, y agrega una nueva matriz al final de la lista cuando la anterior se llena.

Además, como ha conjeturado, el generador de cadenas puede hacer trucos con código “inseguro” para mejorar su rendimiento. Por ejemplo, el código que escribe los datos nuevos en la matriz ya puede haber verificado que la escritura de la matriz estará dentro de los límites. Al apagar el sistema de seguridad, puede evitar la comprobación por escritura de que la inestabilidad podría insertarse para verificar que cada escritura en la matriz sea segura. El generador de cadenas hace varios de estos tipos de trucos para hacer cosas como asegurar que los almacenamientos intermedios se reutilicen en lugar de reasignarse, asegurando que se eviten controles de seguridad innecesarios, y así sucesivamente. Recomiendo contra este tipo de travesuras a menos que sea realmente bueno para escribir código inseguro correctamente, y realmente necesite obtener hasta el último bit de rendimiento.

La implementación de StringBuilder ha cambiado entre versiones, creo. Fundamentalmente, sin embargo, mantiene una estructura mutable de alguna forma. Creo que solía usar una cuerda que todavía estaba siendo mutada (usando métodos internos) y solo me aseguraba de que nunca sería mutada después de que fuera devuelta.

La razón por la que StringBuilder es más rápido que el uso de la concatenación de cadenas en un bucle es precisamente debido a la mutabilidad: no requiere la construcción de una nueva cadena después de cada mutación, lo que significa copiar todos los datos dentro de la cadena, etc.

Para una sola concatenación, en realidad es un poco más eficiente usar + que usar StringBuilder . Es solo cuando estás realizando operaciones múltiples y realmente no necesitas los resultados intermedios que brilla en StringBuilder .

Vea mi artículo en StringBuilder para más información.

Shameless plug: un artículo sobre la implementación de .Net 4 de stringbuilder como una lista vinculada de adjuntos

El Microsoft CLR realiza algunas operaciones con llamadas internas (no exactamente lo mismo que un código inseguro). El mayor beneficio de rendimiento sobre un grupo de cadenas concatenadas es que escribe en un char[] y no crea tantas cadenas intermedias. Cuando llama a ToString (), construye una cadena completa e inmutable de sus contenidos.

StringBuilder utiliza un búfer de cadena que se puede modificar, en comparación con una String normal que no puede ser. Cuando llamas al método ToString de StringBuilder , simplemente congela el búfer de cadena y lo convierte en una cadena normal, por lo que no tiene que copiar todos los datos en un tiempo adicional.

Como StringBuilder puede alterar el búfer de cadena, no tiene que crear un nuevo valor de cadena para cada cambio en los datos de cadena. Cuando utiliza el operador + , el comstackdor lo convierte en una llamada String.Concat que crea un nuevo objeto de cadena. Esta pieza aparentemente inocente de código:

 str += ","; 

comstack en esto:

 str = String.Concat(str, ",");