C # ou Java: preceder strings com StringBuilder?

102

Eu sei que podemos anexar strings usando StringBuilder. Existe uma maneira de acrescentar strings antes de uma string (ou seja, adicionar strings na frente de uma string) usando StringBuilderpara que possamos manter os benefícios de desempenho que isso StringBuilderoferece?

burnt1ce
fonte
Não entendi sua pergunta
Maurice Perry,
5
Prepend. A palavra é prefixada. Anexar uma string deve ser algo como adicionar as duas pontas de uma string de uma vez, não é?
Joel Mueller

Respostas:

29

Anexar uma string normalmente exigirá copiar tudo após o ponto de inserção de volta na matriz de apoio, portanto, não será tão rápido quanto anexar no final.

Mas você pode fazer assim em Java (em C # é o mesmo, mas o método é chamado Insert):

aStringBuilder.insert(0, "newText");
Joachim Sauer
fonte
11

Se você precisar de alto desempenho com muitos prefixos, precisará escrever sua própria versão do StringBuilder(ou usar a de outra pessoa). Com o padrão StringBuilder(embora tecnicamente possa ser implementado de forma diferente), a inserção exige a cópia dos dados após o ponto de inserção. A inserção de n partes de texto pode levar O (n ^ 2) tempo.

Uma abordagem ingênua seria adicionar um deslocamento no char[]buffer de apoio , bem como o comprimento. Quando não houver espaço suficiente para um prefixo, mova os dados mais do que o estritamente necessário. Isso pode trazer o desempenho de volta para O (n log n) (eu acho). Uma abordagem mais refinada é tornar o buffer cíclico. Dessa forma, o espaço livre em ambas as extremidades da matriz torna-se contíguo.

Tom Hawtin - tackline
fonte
5

Você pode tentar um método de extensão:

/// <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!
Mark Maxham
fonte
5

Você pode construir a string ao contrário e depois inverter o resultado. Você incorre em um custo O (n) em vez de um custo O (n ^ 2) no pior caso.

brilhante
fonte
2
Isso só funciona se você estiver acrescentando caracteres individuais. Caso contrário, você precisaria reverter cada string anexada, o que consumiria a maior parte, senão toda a economia, dependendo do tamanho e do número de strings.
Trenó de
4

Eu não usei, mas Ropes For Java parece intrigante. O nome do projeto é um jogo de palavras, use uma corda em vez de uma corda para um trabalho sério. Contorna a penalidade de desempenho para operações de anteposição e outras Vale a pena dar uma olhada, se você vai fazer muito isso.

Uma corda é um substituto de alto desempenho para Strings. A estrutura de dados, descrita em detalhes em "Cordas: uma alternativa para strings", fornece desempenho assintoticamente melhor do que String e StringBuffer para modificações de string comuns como prefixar, acrescentar, excluir e inserir. Como as cordas, as cordas são imutáveis ​​e, portanto, adequadas para uso em programação multi-thread.

Sam Barnum
fonte
4

Aqui está o que você pode fazer se desejar preceder usando a classe StringBuilder do Java:

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

Se bem entendi, o método de inserção parece que fará o que você quiser. Basta inserir a string no deslocamento 0.

Shawn
fonte
2

Tente usar Insert ()

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

A julgar pelos outros comentários, não há uma maneira rápida padrão de fazer isso. Usar StringBuilder .Insert(0, "text")é aproximadamente apenas 1-3x mais rápido do que usar concatenação de String dolorosamente lenta (com base em> 10.000 concats), portanto, abaixo está uma classe para prefixar potencialmente milhares de vezes mais rápido!

Eu incluí algumas outras funcionalidades básicas, tais como append(), subString()e length()etc. Ambos os Anexa e prepends variar de cerca de duas vezes mais rápido para 3x mais lento do que StringBuilder anexa. Como StringBuilder, o buffer nesta classe aumentará automaticamente quando o texto ultrapassar o tamanho do buffer antigo.

O código foi bastante testado, mas não posso garantir que esteja livre de bugs.

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)
    }
}
Dan W
fonte
1

Você mesmo poderia criar uma extensão para StringBuilder com uma classe simples:

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
    }
}

Depois, basta adicionar:

using Application.Code.Helpers;

No início de qualquer classe em que você deseja usar o StringBuilder e sempre que usar o intelli-sense com uma variável StringBuilder, os métodos Prepend e PrependLine serão exibidos. Lembre-se de que, ao usar o Prepend, você precisará fazer o Prepend na ordem reversa do que se estivesse no Anexing.

ScubaSteve
fonte
0

Isso deve funcionar:

aStringBuilder = "newText" + aStringBuilder; 
gok-nine
fonte
No .NET, isso funciona perfeitamente com valores do tipo string, mas não funciona com valores do tipo StringBuilder. A resposta de @ScubaSteve funciona bem.
Contango,