C # ou Java: ajouter des chaînes avec StringBuilder?

102

Je sais que nous pouvons ajouter des chaînes en utilisant StringBuilder. Existe-t-il un moyen de pré-ajouter des chaînes (c'est-à-dire d'ajouter des chaînes devant une chaîne) en utilisant StringBuilderafin de pouvoir conserver les avantages de performances que cela StringBuilderoffre?

burnt1ce
la source
Je ne comprends pas votre question
Maurice Perry
5
Prepend. Le mot est préfixé. Préajouter une chaîne doit être quelque chose comme l'ajout aux deux extrémités d'une chaîne à la fois, je suppose?
Joel Mueller

Réponses:

29

La pré-attente d'une chaîne nécessitera généralement de copier tout après le point d'insertion en arrière dans le tableau de sauvegarde, donc ce ne sera pas aussi rapide que l'ajout à la fin.

Mais vous pouvez le faire comme ceci en Java (en C # c'est la même chose, mais la méthode est appelée Insert):

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

Si vous avez besoin de hautes performances avec beaucoup de préfixes, vous devrez écrire votre propre version de StringBuilder(ou utiliser celle de quelqu'un d'autre). Avec la norme StringBuilder(bien que techniquement, cela puisse être implémenté différemment), l'insertion nécessite de copier les données après le point d'insertion. L'insertion de n morceaux de texte peut prendre O (n ^ 2) temps.

Une approche naïve consisterait à ajouter un décalage dans le char[]tampon de support ainsi que la longueur. Lorsqu'il n'y a pas assez de place pour un préfixe, déplacez les données plus que strictement nécessaire. Cela peut ramener les performances à O (n log n) (je pense). Une approche plus raffinée consiste à rendre le tampon cyclique. De cette manière, l'espace disponible aux deux extrémités du réseau devient contigu.

Tom Hawtin - Tacle
la source
5

Vous pouvez essayer une méthode d'extension:

/// <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
la source
5

Vous pouvez créer la chaîne à l'envers, puis inverser le résultat. Vous engagez un coût O (n) au lieu d'un coût O (n ^ 2) dans le pire des cas.

brillant
la source
2
Cela ne fonctionne que si vous ajoutez des caractères individuels. Sinon, vous devrez inverser chaque chaîne que vous avez ajoutée, ce qui consommerait la plupart sinon la totalité des économies en fonction de la taille et du nombre de chaînes.
Traîneau le
4

Je ne l'ai pas utilisé mais Ropes For Java semble intriguant. Le nom du projet est un jeu de mots, utilisez une corde au lieu d'une chaîne pour un travail sérieux. Contourne la pénalité des performances pour les opérations en préfixe et autres. Vaut le détour, si vous allez faire beaucoup de ça.

Une corde est un remplacement de haute performance pour les cordes. La structure de données, décrite en détail dans «Ropes: an Alternative to Strings», offre des performances asymptotiquement meilleures que String et StringBuffer pour les modifications de chaînes courantes telles que le préfixe, l'ajout, la suppression et l'insertion. Comme les cordes, les cordes sont immuables et donc bien adaptées à une utilisation dans la programmation multi-thread.

Sam Barnum
la source
4

Voici ce que vous pouvez faire si vous souhaitez ajouter un préfixe à l'aide de la classe StringBuilder de Java:

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

Si je vous comprends bien, la méthode d'insertion semble faire ce que vous voulez. Insérez simplement la chaîne à l'offset 0.

Shawn
la source
2

Essayez d'utiliser Insert ()

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

À en juger par les autres commentaires, il n'y a pas de moyen rapide standard de le faire. L'utilisation de StringBuilder .Insert(0, "text")est environ 1 à 3 fois plus rapide que l'utilisation d'une concaténation de chaînes extrêmement lente (basée sur plus de 10000 concats), vous trouverez donc ci-dessous une classe à ajouter potentiellement des milliers de fois plus rapidement!

J'ai inclus d'autres fonctionnalités de base telles que append(), subString()et length()etc. Les deux et précèder varient y ajouter ses environ deux fois plus vite à 3 fois plus lent que StringBuilder ajoute. Comme StringBuilder, la mémoire tampon de cette classe augmente automatiquement lorsque le texte dépasse l'ancienne taille de la mémoire tampon.

Le code a été testé beaucoup, mais je ne peux pas garantir qu'il soit exempt de bogues.

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
la source
1

Vous pouvez créer vous-même une extension pour StringBuilder avec une classe 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
    }
}

Ensuite, ajoutez simplement:

using Application.Code.Helpers;

En haut de toute classe dans laquelle vous souhaitez utiliser StringBuilder et chaque fois que vous utilisez intelli-sense avec une variable StringBuilder, les méthodes Prepend et PrependLine s'affichent. N'oubliez pas que lorsque vous utilisez Prepend, vous devrez ajouter Prepend dans l'ordre inverse de celui que vous aviez ajouté.

ScubaSteve
la source
0

Cela devrait fonctionner:

aStringBuilder = "newText" + aStringBuilder; 
gok-neuf
la source
Dans .NET, cela fonctionne parfaitement avec les valeurs de type string, mais ne fonctionne pas avec les valeurs de type StringBuilder. La réponse de @ScubaSteve fonctionne bien.
Contango