C # oder Java: Strings mit StringBuilder voranstellen?


102

Ich weiß, dass wir Zeichenfolgen mit anhängen können StringBuilder. Gibt es eine Möglichkeit, Zeichenfolgen voranzustellen (dh Zeichenfolgen vor einer Zeichenfolge hinzuzufügen), StringBuilderum die Leistungsvorteile beizubehalten, die sich StringBuilderbieten?


Ich verstehe Ihre Frage nicht
Maurice Perry

5
Vorbereiten. Das Wort steht vor. Das Voranstellen einer Zeichenfolge muss so etwas wie das gleichzeitige Hinzufügen an beiden Enden einer Zeichenfolge sein, denke ich?
Joel Mueller

Antworten:



29

Das Voranstellen eines Strings erfordert normalerweise das Kopieren aller Elemente nach dem Einfügepunkt zurück in das Backing-Array, sodass es nicht so schnell ist wie das Anhängen an das Ende.

Aber Sie können es in Java so machen (in C # ist es dasselbe, aber die Methode wird aufgerufen Insert):

aStringBuilder.insert(0, "newText");

11

Wenn Sie eine hohe Leistung mit vielen Voranmeldungen benötigen, müssen Sie Ihre eigene Version von schreiben StringBuilder(oder die einer anderen verwenden). Beim Standard StringBuilder(obwohl technisch könnte es anders implementiert werden) müssen beim Einfügen Daten nach der Einfügemarke kopiert werden. Das Einfügen von n Textstücken kann O (n ^ 2) dauern.

Ein naiver Ansatz wäre, dem Offset- char[]Puffer einen Offset sowie die Länge hinzuzufügen . Wenn nicht genügend Platz für ein Präfix vorhanden ist, verschieben Sie die Daten um mehr als unbedingt erforderlich. Dies kann die Leistung wieder auf O (n log n) bringen (glaube ich). Ein verfeinerter Ansatz besteht darin, den Puffer zyklisch zu machen. Auf diese Weise wird der freie Speicherplatz an beiden Enden des Arrays zusammenhängend.


5

Sie könnten eine Erweiterungsmethode ausprobieren:

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

Sie können den String in umgekehrter Reihenfolge erstellen und dann das Ergebnis umkehren. Ihnen entstehen O (n) -Kosten anstelle von O (n ^ 2) -West-Case-Kosten.


2
Dies funktioniert nur, wenn Sie einzelne Zeichen anhängen. Andernfalls müssten Sie jede angehängte Zeichenfolge umkehren, was je nach Größe und Anzahl der Zeichenfolgen die meisten, wenn nicht sogar alle Einsparungen verschlingt.
Schlitten

4

Ich habe es nicht benutzt, aber Ropes For Java klingt faszinierend. Der Projektname ist ein Wortspiel. Verwenden Sie für ernsthafte Arbeiten ein Seil anstelle eines Strings . Umgeht die Leistungseinbußen bei Voranstellungen und anderen Vorgängen. Einen Blick wert, wenn Sie viel davon tun werden.

Ein Seil ist ein Hochleistungsersatz für Saiten. Die Datenstruktur, die ausführlich in "Seile: eine Alternative zu Zeichenfolgen" beschrieben wird, bietet eine asymptotisch bessere Leistung als String und StringBuffer für allgemeine Änderungen an Zeichenfolgen wie Voranstellen, Anhängen, Löschen und Einfügen. Wie Strings sind Seile unveränderlich und daher für die Verwendung in der Multithread-Programmierung gut geeignet.


4

Folgendes können Sie tun, wenn Sie die StringBuilder-Klasse von Java voranstellen möchten:

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

3

Wenn ich Sie richtig verstehe, sieht die Einfügemethode so aus, als würde sie das tun, was Sie wollen. Fügen Sie einfach die Zeichenfolge mit dem Versatz 0 ein.


2

Versuchen Sie es mit Insert ()

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

2

Den anderen Kommentaren nach zu urteilen, gibt es keinen normalen schnellen Weg, dies zu tun. Verwenden von StringBuilder.Insert(0, "text") ist ungefähr nur 1-3x so schnell wie die Verwendung einer schmerzhaft langsamen String-Verkettung (basierend auf> 10000 Concats). Im Folgenden finden Sie eine Klasse, die möglicherweise tausendmal schneller vorangestellt werden kann!

Ich habe einige andere grundlegende Funktionen wie eingeschlossen append(), subString()undlength() usw. Beide Appends und wird vorangestellt variieren von etwa doppelt so schnell zu 3x langsamer als String anhängt. Wie bei StringBuilder erhöht sich der Puffer in dieser Klasse automatisch, wenn der Text die alte Puffergröße überschreitet.

Der Code wurde ziemlich oft getestet, aber ich kann nicht garantieren, dass er frei von Fehlern ist.

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

Sie können selbst eine Erweiterung für StringBuilder mit einer einfachen Klasse erstellen:

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

Dann fügen Sie einfach hinzu:

using Application.Code.Helpers;

An der Spitze jeder Klasse, in der Sie den StringBuilder verwenden möchten, und jedes Mal, wenn Sie Intelli-Sense mit einer StringBuilder-Variablen verwenden, werden die Methoden Prepend und PrependLine angezeigt. Denken Sie daran, dass Sie bei Verwendung von Voranstellen in umgekehrter Reihenfolge voranstellen müssen, als wenn Sie Anhängen.


0

Das sollte funktionieren:

aStringBuilder = "newText" + aStringBuilder; 

In .NET funktioniert dies perfekt mit Typwerten string, jedoch nicht mit Typwerten StringBuilder. Die Antwort von @ScubaSteve funktioniert gut.
Contango
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.