C # или Java: добавить строки с помощью StringBuilder?

104

Я знаю, что мы можем добавлять строки, используя StringBuilder. Есть ли способ, которым мы можем добавить строки (т.е. добавить строки перед строкой), используя, StringBuilderчтобы мы могли сохранить предлагаемые преимущества производительности StringBuilder?

Burnt1ce
источник
Я не понимаю вашего вопроса
Морис Перри
5
Подготовить. Слово добавлено. Полагаю, предварительное добавление строки должно быть чем-то вроде добавления сразу к обоим концам строки?
Джоэл Мюллер,

Ответы:

166

Использование метода вставки с параметром позиции, установленным на 0, будет таким же, как добавление в начале (т.е. вставка в начало).

Пример: varStringBuilder.insert(0, "someThing");

Работает как для C #, так и для Java.

Driis
источник
7
Вставка StringBuilder
Мэтью Фарвелл,
Правильный JavaDoc для соответствующего API: docs.oracle.com/javase/1.5.0/docs/api/java/lang/…
Sled
29

Добавление строки обычно требует копирования всего, что находится после точки вставки, обратно в массиве поддержки, поэтому это будет не так быстро, как добавление в конец.

Но в Java можно сделать так (в C # то же самое, но метод вызывается Insert):

aStringBuilder.insert(0, "newText");
Иоахим Зауэр
источник
11

Если вам требуется высокая производительность с большим количеством препендов, вам нужно будет написать свою собственную версию StringBuilder(или использовать чужую). Со стандартомStringBuilder вставке (хотя технически она может быть реализована иначе) для вставки требуется копирование данных после точки вставки. Вставка n фрагментов текста может занять время O (n ^ 2).

Наивный подход заключался бы в добавлении смещения в char[]буфер поддержки, а также длины. Если места для добавления не хватает, переместите данные на большее значение, чем это строго необходимо. Это может снизить производительность до O (n log n) (я думаю). Более совершенный подход - сделать буфер циклическим. Таким образом, свободное пространство на обоих концах массива становится непрерывным.

Том Хотин - tackline
источник
5

Вы можете попробовать метод расширения:

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

Вы можете построить строку в обратном порядке, а затем обратить результат. Вы понесете стоимость O (n) вместо затрат O (n ^ 2) в худшем случае.

яркий
источник
2
Это работает, только если вы добавляете отдельные символы. В противном случае вам придется перевернуть каждую добавленную вами строку, что съест большую, если не всю экономию в зависимости от размера и количества строк.
Sled
4

Я не использовал его, но Ropes For Java звучит интригующе. Название проекта - игра слов, для серьезной работы используйте веревку вместо струны . Обходит снижение производительности за добавление и другие операции. Стоит взглянуть, если вы собираетесь много заниматься этим.

Веревка - это эффективная замена струнам. Структура данных, подробно описанная в статье «Веревки: альтернатива строкам», обеспечивает асимптотически лучшую производительность, чем String и StringBuffer, для общих модификаций строк, таких как добавление, добавление, удаление и вставка. Как и строки, связки неизменяемы и поэтому хорошо подходят для использования в многопоточном программировании.

Сэм Барнум
источник
4

Вот что вы можете сделать, если хотите добавить в начало класса Java StringBuilder:

StringBuilder str = new StringBuilder();
str.Insert(0, "text");
s_hewitt
источник
3

Если я вас правильно понял, похоже, метод вставки сделает то, что вы хотите. Просто вставьте строку со смещением 0.

Шон
источник
2

Попробуйте использовать Insert ()

StringBuilder MyStringBuilder = new StringBuilder("World!");
MyStringBuilder.Insert(0,"Hello "); // Hello World!
Boj
источник
2

Судя по другим комментариям, стандартного быстрого способа сделать это не существует. Использование StringBuilder .Insert(0, "text")примерно в 1–3 раза быстрее, чем использование мучительно медленной конкатенации строк (на основе> 10000 конкатенаций), поэтому ниже приведен класс, который потенциально может добавлять в тысячи раз быстрее!

Я включил некоторые другие базовые функции, такие как append(), subString()и length()т. Д. И добавление, и добавление различаются от примерно в два раза быстрее до 3 раз медленнее, чем добавления StringBuilder. Как и StringBuilder, буфер в этом классе автоматически увеличивается, когда текст превышает старый размер буфера.

Код был протестирован достаточно много, но я не могу гарантировать, что он не содержит ошибок.

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

Вы можете сами создать расширение для StringBuilder с помощью простого класса:

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

Затем просто добавьте:

using Application.Code.Helpers;

В верхней части любого класса, в котором вы хотите использовать StringBuilder, и каждый раз, когда вы используете intelli-sense с переменной StringBuilder, будут отображаться методы Prepend и PrependLine. Просто помните, что, когда вы используете Prepend, вам нужно будет Prepend в обратном порядке, чем если бы вы использовали Appending.

АквалангСтив
источник
0

Это должно работать:

aStringBuilder = "newText" + aStringBuilder; 
гок-девять
источник
В .NET это прекрасно работает со значениями типа string, но не работает со значениями типа StringBuilder. Ответ от @ScubaSteve работает хорошо.
Contango