Я знаю, що ми можемо додавати рядки за допомогою StringBuilder
. Чи існує спосіб, коли ми можемо додавати рядки (тобто додавати рядки перед рядком), використовуючи, StringBuilder
щоб ми могли зберегти переваги від продуктивності StringBuilder
?
Я знаю, що ми можемо додавати рядки за допомогою StringBuilder
. Чи існує спосіб, коли ми можемо додавати рядки (тобто додавати рядки перед рядком), використовуючи, StringBuilder
щоб ми могли зберегти переваги від продуктивності StringBuilder
?
Відповіді:
Використання методу вставки з параметром позиції, встановленим на 0, буде таким самим, як і попереднє (тобто вставлення на початку).
Приклад: varStringBuilder.insert(0, "someThing");
Заздалегідь додавання рядка, як правило, вимагатиме копіювати все після того, як точка вставки повернеться в резервний масив, тому це не буде настільки швидко, як додавання до кінця.
Але ви можете зробити це так у Java (у C # це те саме, але метод називається Insert
):
aStringBuilder.insert(0, "newText");
Якщо вам потрібна висока продуктивність з великими претензіями, вам потрібно буде написати свою власну версію StringBuilder
(або використовувати чужу). За допомогою стандарту StringBuilder
(хоча технічно це може бути реалізовано інакше) вставка вимагає копіювання даних після точки вставки. Вставка n тексту тексту може зайняти O (n ^ 2) час.
Наївним підходом було б додати зміщення в резервний char[]
буфер, а також довжину. Якщо для наплати не вистачає місця, перемістіть дані вгору більше, ніж це суворо необхідно. Це може повернути продуктивність до O (n log n) (я думаю). Більш досконалий підхід полягає в тому, щоб зробити буфер циклічним. Таким чином запасний простір на обох кінцях масиву стає суміжним.
Ви можете спробувати метод розширення:
/// <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!
Ви можете побудувати рядок у зворотному порядку, а потім повернути результат. Ви несете O (n) вартість замість O (n ^ 2) найгіршої вартості.
Я не використовував його, але інтригує звуки Ropes For Java . Назва проекту - це гра на слова, використовуйте мотузку замість струни для серйозної роботи. Отримує покарання за виконання за попередні та інші операції. Варто поглянути, якщо ви збираєтеся багато цього робити.
Мотузка - це високоефективна заміна струн. Структура даних, детально описана в "Ropes: Альтернатива струнним", забезпечує асимптотично більшу продуктивність, ніж String і StringBuffer для звичайних модифікацій рядків, таких як додавання, додавання, видалення та вставка. Як і струнні, мотузки незмінні і тому добре підходять для використання в багатопотоковому програмуванні.
Якщо я вас правильно зрозумів, метод вставки виглядає так, що він буде робити те, що ви хочете. Просто вставте рядок зі зміщенням 0.
Судячи з інших коментарів, немає стандартного швидкого способу зробити це. Використання StringBuilder's .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)
}
}
Ви можете створити розширення для 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 в зворотному порядку, ніж якщо ви додавали.
Це має працювати:
aStringBuilder = "newText" + aStringBuilder;
string
, але не працює зі значеннями типу StringBuilder
. Відповідь від @ScubaSteve працює добре.