C # o Java: ¿anteponer cadenas con StringBuilder?


102

Sé que podemos agregar cadenas usando StringBuilder. ¿Hay alguna forma en que podamos anteponer cadenas (es decir, agregar cadenas delante de una cadena) utilizando StringBuilderpara poder mantener los beneficios de rendimiento que StringBuilderofrece?


No entiendo tu pregunta
Maurice Perry

5
Anteponer. La palabra se antepone. Supongo que agregar previamente una cadena debe ser algo así como agregar ambos extremos de una cadena a la vez, supongo.
Joel Mueller

Respuestas:



29

Anteponer una cadena generalmente requerirá copiar todo después del punto de inserción de nuevo en la matriz de respaldo, por lo que no será tan rápido como agregar al final.

Pero puedes hacerlo así en Java (en C # es lo mismo, pero se llama al método Insert):

aStringBuilder.insert(0, "newText");

11

Si necesita un alto rendimiento con muchos prependidos, deberá escribir su propia versión de StringBuilder(o usar la de otra persona). Con el estándar StringBuilder(aunque técnicamente podría implementarse de manera diferente), la inserción requiere copiar los datos después del punto de inserción. Insertar n fragmentos de texto puede llevar O (n ^ 2) tiempo.

Un enfoque ingenuo sería agregar un desplazamiento en el char[]búfer de respaldo , así como la longitud. Cuando no haya suficiente espacio para un prefijo, suba los datos más de lo estrictamente necesario. Esto puede reducir el rendimiento a O (n log n) (creo). Un enfoque más refinado es hacer que el búfer sea cíclico. De esa manera, el espacio libre en ambos extremos de la matriz se vuelve contiguo.


5

Puedes probar un método de extensión:

/// <summary>
/// kind of a dopey little one-off for StringBuffer, but 
/// an example where you can get crazy with extension methods
/// </summary>
public static void Prepend(this StringBuilder sb, string s)
{
    sb.Insert(0, s);
}

StringBuilder sb = new StringBuilder("World!");
sb.Prepend("Hello "); // Hello World!

5

Puede construir la cadena al revés y luego invertir el resultado. Usted incurre en un costo O (n) en lugar de un costo en el peor de los casos O (n ^ 2).


2
Eso solo funciona si agrega caracteres individuales. De lo contrario, necesitaría invertir cada cadena que agregó, lo que consumiría la mayoría, si no todos, los ahorros según el tamaño y la cantidad de cadenas.
Trineo

4

No lo he usado, pero Ropes For Java suena intrigante. El nombre del proyecto es un juego de palabras, use una cuerda en lugar de una cuerda para un trabajo serio. Evita la penalización de rendimiento por anteponer y otras operaciones. Vale la pena echarle un vistazo, si vas a hacer mucho de esto.

Una cuerda es un reemplazo de alto rendimiento para cuerdas. La estructura de datos, descrita en detalle en "Cuerdas: una alternativa a las cadenas", proporciona un rendimiento asintóticamente mejor que String y StringBuffer para modificaciones comunes de cadenas como anteponer, añadir, eliminar e insertar. Al igual que las cuerdas, las cuerdas son inmutables y, por lo tanto, son adecuadas para su uso en programación multiproceso.


4

Esto es lo que puede hacer si desea anteponer el uso de la clase StringBuilder de Java:

StringBuilder str = new StringBuilder();
str.Insert(0, "text");


2

Intente usar Insertar ()

StringBuilder MyStringBuilder = new StringBuilder("World!");
MyStringBuilder.Insert(0,"Hello "); // Hello World!

2

A juzgar por los otros comentarios, no existe una forma rápida estándar de hacer esto. Usar StringBuilder's .Insert(0, "text")es aproximadamente solo 1-3 veces más rápido que usar una concatenación de cadenas dolorosamente lenta (basada en> 10000 concats), por lo que a continuación se muestra una clase para anteponer potencialmente miles de veces más rápido.

He incluido alguna otra funcionalidad básica como append(), subString()y length()etc. Tanto los anexos como los antepuestos varían desde aproximadamente el doble de rápido hasta 3 veces más lento que los anexos de StringBuilder. Como StringBuilder, el búfer en esta clase aumentará automáticamente cuando el texto desborde el tamaño del búfer anterior.

El código se ha probado bastante, pero no puedo garantizar que esté libre de errores.

class Prepender
{
    private char[] c;
    private int growMultiplier;
    public int bufferSize;      // Make public for bug testing
    public int left;            // Make public for bug testing
    public int right;           // Make public for bug testing
    public Prepender(int initialBuffer = 1000, int growMultiplier = 10)
    {
        c = new char[initialBuffer];
        //for (int n = 0; n < initialBuffer; n++) cc[n] = '.';  // For debugging purposes (used fixed width font for testing)
        left = initialBuffer / 2;
        right = initialBuffer / 2;
        bufferSize = initialBuffer;
        this.growMultiplier = growMultiplier;
    }
    public void clear()
    {
        left = bufferSize / 2;
        right = bufferSize / 2;
    }
    public int length()
    {
        return right - left;
    }

    private void increaseBuffer()
    {
        int nudge = -bufferSize / 2;
        bufferSize *= growMultiplier;
        nudge += bufferSize / 2;
        char[] tmp = new char[bufferSize];
        for (int n = left; n < right; n++) tmp[n + nudge] = c[n];
        left += nudge;
        right += nudge;
        c = new char[bufferSize];
        //for (int n = 0; n < buffer; n++) cc[n]='.';   // For debugging purposes (used fixed width font for testing)
        for (int n = left; n < right; n++) c[n] = tmp[n];
    }

    public void append(string s)
    {
        // If necessary, increase buffer size by growMultiplier
        while (right + s.Length > bufferSize) increaseBuffer();

        // Append user input to buffer
        int len = s.Length;
        for (int n = 0; n < len; n++)
        {
            c[right] = s[n];
            right++;
        }
    }
    public void prepend(string s)
    {
        // If necessary, increase buffer size by growMultiplier
        while (left - s.Length < 0) increaseBuffer();               

        // Prepend user input to buffer
        int len = s.Length - 1;
        for (int n = len; n > -1; n--)
        {
            left--;
            c[left] = s[n];
        }
    }
    public void truncate(int start, int finish)
    {
        if (start < 0) throw new Exception("Truncation error: Start < 0");
        if (left + finish > right) throw new Exception("Truncation error: Finish > string length");
        if (finish < start) throw new Exception("Truncation error: Finish < start");

        //MessageBox.Show(left + " " + right);

        right = left + finish;
        left = left + start;
    }
    public string subString(int start, int finish)
    {
        if (start < 0) throw new Exception("Substring error: Start < 0");
        if (left + finish > right) throw new Exception("Substring error: Finish > string length");
        if (finish < start) throw new Exception("Substring error: Finish < start");
        return toString(start,finish);
    }

    public override string ToString()
    {
        return new string(c, left, right - left);
        //return new string(cc, 0, buffer);     // For debugging purposes (used fixed width font for testing)
    }
    private string toString(int start, int finish)
    {
        return new string(c, left+start, finish-start );
        //return new string(cc, 0, buffer);     // For debugging purposes (used fixed width font for testing)
    }
}

1

Puede crear una extensión para StringBuilder usted mismo con una clase simple:

namespace Application.Code.Helpers
{
    public static class StringBuilderExtensions
    {
        #region Methods

        public static void Prepend(this StringBuilder sb, string value)
        {
            sb.Insert(0, value);
        }

        public static void PrependLine(this StringBuilder sb, string value)
        {
            sb.Insert(0, value + Environment.NewLine);
        }

        #endregion
    }
}

Luego, solo agrega:

using Application.Code.Helpers;

En la parte superior de cualquier clase en la que desee usar StringBuilder y cada vez que use intelli-sense con una variable StringBuilder, aparecerán los métodos Prepend y PrependLine. Solo recuerde que cuando use Anteponer, deberá anteponer en orden inverso que si estuviera anexando.


0

Esto debería funcionar:

aStringBuilder = "newText" + aStringBuilder; 

En .NET, esto funciona perfectamente con valores de tipo string, pero no funciona con valores de tipo StringBuilder. La respuesta de @ScubaSteve funciona bien.
Contango
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.