En C# la estrategia utilizada para hacer crecer el búfer interno utilizado por un StringBuilder ha cambiado con el tiempo.
Existen tres estrategias básicas para resolver este problema, y tienen diferentes características de rendimiento.
La primera estrategia básica es:
- Hacer una serie de caracteres
- Cuando se acaba el espacio, crear una nueva matriz con k más caracteres, por alguna constante k.
- Copie la matriz anterior a la nueva matriz y huérfana la matriz anterior.
Esta estrategia tiene una serie de problemas, el más evidente de los cuales es que es O (n 2 ) en el tiempo si la cadena que se está construyendo es extremadamente grande. Digamos que k tiene mil caracteres y la cadena final tiene un millón de caracteres. Terminas reasignando la cadena a 1000, 2000, 3000, 4000, ... y, por lo tanto, copiando 1000 + 2000 + 3000 + 4000 + ... + 999000 caracteres, ¡lo que equivale al orden de 500 mil millones de caracteres copiados!
Esta estrategia tiene la bonita propiedad de que la cantidad de memoria "desperdiciada" está limitada por k.
En la práctica, esta estrategia rara vez se utiliza debido a ese problema n-cuadrado.
La segunda estrategia básica es
- Hacer una gama
- Cuando se acaba el espacio, crear una nueva matriz con k% más caracteres, por alguna constante k.
- Copie la matriz anterior a la nueva matriz y huérfana la matriz anterior.
k% suele ser 100%; si es así, esto se denomina estrategia "doble cuando está lleno".
Esta estrategia tiene la buena propiedad de que su costo amortizado es O (n). Supongamos nuevamente que la última cuerda es un millón de caracteres y comienzas con mil. Hace copias en 1000, 2000, 4000, 8000, ... y termina copiando 1000 + 2000 + 4000 + 8000 ... + 512000 caracteres, que suman alrededor de un millón de caracteres copiados; mucho mejor.
La estrategia tiene la propiedad de que el costo amortizado es lineal sin importar el porcentaje que elija.
Esta estrategia tiene una serie de pega que veces una operación de copia es extremadamente caro y que puede estar perdiendo hasta k% de la longitud de la cadena final en la memoria no utilizada.
La tercera estrategia es hacer una lista vinculada de matrices, cada matriz de tamaño k. Cuando desborda una matriz existente, se asigna una nueva y se agrega al final de la lista.
Esta estrategia tiene la buena propiedad de que ninguna operación es particularmente costosa, la memoria desperdiciada total está limitada por k, y no es necesario que pueda ubicar grandes bloques en el montón de forma regular. Tiene la desventaja de que, finalmente, convertirlo en una cadena puede ser costoso, ya que las matrices de la lista enlazada podrían tener una ubicación pobre.
El generador de cadenas en .NET Framework solía usar una estrategia de doble cuando completo; ahora usa una estrategia de lista enlazada de bloques.
IIRC, .NET StringBuilder duplicará al menos su tamaño actual de búfer si intenta agregar algo que requerirá un aumento de tamaño. –
La cantidad matematical es 1.6180339887 ...: la proporción áurea, * pero solo usa 2 * – pmg
@ChrisFarmer: Esa fue la estrategia en el pasado; la versión actual usa una estrategia diferente. –