C # o Java: anteporre le stringhe con StringBuilder?


102

So che possiamo aggiungere stringhe usando StringBuilder. C'è un modo in cui possiamo anteporre le stringhe (cioè aggiungere stringhe davanti a una stringa) usando in StringBuildermodo da poter mantenere i vantaggi in termini di prestazioni che StringBuilderoffre?


Non capisco la tua domanda
Maurice Perry

5
Anteporre. La parola è anteposta. La premessa di una stringa deve essere qualcosa come l'aggiunta a entrambe le estremità di una stringa contemporaneamente, immagino?
Joel Mueller

Risposte:



29

L'anticipazione di una stringa di solito richiede la copia di tutto dopo il punto di inserimento indietro nell'array di supporto, quindi non sarà così veloce come aggiungerlo alla fine.

Ma puoi farlo in questo modo in Java (in C # è lo stesso, ma il metodo viene chiamato Insert):

aStringBuilder.insert(0, "newText");

11

Se hai bisogno di prestazioni elevate con molti prefissi, dovrai scrivere la tua versione di StringBuilder(o usare quella di qualcun altro). Con lo standard StringBuilder(anche se tecnicamente potrebbe essere implementato in modo diverso) l'inserimento richiede la copia dei dati dopo il punto di inserimento. L'inserimento di n parti di testo può richiedere O (n ^ 2) tempo.

Un approccio ingenuo sarebbe quello di aggiungere un offset nel char[]buffer di supporto oltre alla lunghezza. Quando non c'è abbastanza spazio per un anteporre, spostare i dati più in alto di quanto strettamente necessario. Questo può riportare le prestazioni a O (n log n) (credo). Un approccio più raffinato consiste nel rendere ciclico il buffer. In questo modo lo spazio libero su entrambe le estremità dell'array diventa contiguo.


5

Potresti provare un metodo di estensione:

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

Potresti costruire la stringa al contrario e quindi invertire il risultato. Si sostiene un costo O (n) invece di un costo O (n ^ 2) nel caso peggiore.


2
Funziona solo se stai aggiungendo singoli caratteri. Altrimenti dovresti invertire ogni stringa che hai aggiunto che consumerebbe la maggior parte se non tutti i risparmi a seconda delle dimensioni e del numero di stringhe.
Slitta

4

Non l'ho usato ma Ropes For Java suona intrigante. Il nome del progetto è un gioco di parole, usa una corda invece di una stringa per un lavoro serio. Elimina la penalizzazione delle prestazioni per operazioni preliminari e altre operazioni. Vale la pena dare un'occhiata, se hai intenzione di fare molto di questo.

Una corda è un sostituto ad alte prestazioni per le corde. La struttura dei dati, descritta in dettaglio in "Corde: un'alternativa alle stringhe", fornisce prestazioni asintoticamente migliori rispetto sia a String che a StringBuffer per modifiche di stringhe comuni come anteporre, aggiungere, eliminare e inserire. Come le corde, le corde sono immutabili e quindi adatte per l'uso nella programmazione multi-thread.


4

Ecco cosa puoi fare se vuoi anteporre usando la classe StringBuilder di Java:

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


2

Prova a usare Insert ()

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

2

A giudicare dagli altri commenti, non esiste un modo rapido standard per farlo. L'uso di StringBuilder .Insert(0, "text")è approssimativamente solo 1-3 volte più veloce dell'uso della concatenazione di stringhe dolorosamente lenta (basata su> 10000 concatenati), quindi di seguito è riportata una classe da anteporre potenzialmente migliaia di volte più velocemente!

Ho incluso alcune altre funzionalità di base come append(), subString()e così via length(). Sia le aggiunte che le anteprime variano da circa due volte più veloci a 3 volte più lente di StringBuilder. Come StringBuilder, il buffer in questa classe aumenterà automaticamente quando il testo supera la vecchia dimensione del buffer.

Il codice è stato testato parecchio, ma non posso garantire che sia privo di bug.

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

Puoi creare tu stesso un'estensione per StringBuilder con una semplice classe:

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

Quindi, aggiungi semplicemente:

using Application.Code.Helpers;

All'inizio di qualsiasi classe in cui si desidera utilizzare StringBuilder e ogni volta che si utilizza intelli-sense con una variabile StringBuilder, verranno visualizzati i metodi Prepend e PrependLine. Ricorda solo che quando usi Anteponi, dovrai anteporre in ordine inverso rispetto a se stessi aggiungendo.


0

Questo dovrebbe funzionare:

aStringBuilder = "newText" + aStringBuilder; 

In .NET, funziona perfettamente con i valori di tipo string, ma non funziona con i valori di tipo StringBuilder. La risposta di @ScubaSteve funziona bene.
Contango
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.