String vs. StringBuilder

215

Я понимаю разницу между Stringи StringBuilder( StringBuilderбудучи изменяемым), но есть ли большая разница в производительности между ними?

В программе, над которой я работаю, есть много добавлений строк (500+). Используете StringBuilderли вы лучший выбор?

KUVO
источник

Ответы:

236

Да, разница в производительности значительна. См. Статью базы знаний « Как улучшить производительность конкатенации строк в Visual C # ».

Я всегда пытался сначала написать код для ясности, а потом оптимизировать для производительности. Это гораздо проще, чем сделать это наоборот! Однако, увидев огромную разницу в производительности в моих приложениях между этими двумя, я теперь думаю об этом немного более тщательно.

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

Джей Базузи
источник
44
Хорошее практическое правило - использовать Strings, если вы не собираетесь их менять, и использовать StringBuilder, если вы будете их менять.
Тим
31
Мне очень нравится этот ответ, особенно совет, чтобы код для ясности перед производительностью. Как разработчики, мы тратим столько же или больше времени на чтение кода, как и на его написание.
Скотт Лоуренс
Outlaw: Я думаю, что это должно стать собственным ответом, за который проголосуют отдельно, если я правильно понимаю StackOverflow.
Джей Базузи
2
исходя из моего опыта, если вы пытаетесь объединить около 10-15 строк, то вы можете пойти со строкой, но если нет. строк больше, чем тогда, используйте строителя строк
Peeyush
3
Это все еще зависит от того, как его использовать, для ссылки на реальные тесты мне нравится Coding Horror - Печальная трагедия театра микрооптимизации
Erik Philips
56

Чтобы уточнить, что сказала Джиллиан о 4-й строке, если у вас есть что-то вроде этого:

string a,b,c,d;
 a = b + c + d;

тогда было бы быстрее использовать строки и оператор плюс. Это потому, что (как и Java, как указывает Эрик), он автоматически использует StringBuilder (на самом деле, он использует примитив, который также использует StringBuilder)

Однако, если то, что вы делаете, ближе к:

string a,b,c,d;
 a = a + b;
 a = a + c;
 a = a + d;

Тогда вам нужно явно использовать StringBuilder. .Net не создает автоматически StringBuilder здесь, потому что это было бы бессмысленно. В конце каждой строки «a» должна быть (неизменяемой) строкой, поэтому она должна создавать и размещать StringBuilder в каждой строке. Для скорости вам нужно будет использовать тот же StringBuilder, пока вы не закончите сборку:

string a,b,c,d;
StringBuilder e = new StringBuilder();
 e.Append(b);
 e.Append(c);
 e.Append(d);
 a = e.ToString();
Джеймс Керран
источник
5
Нет причины, по которой компилятору C # нужно относиться ко второму образцу иначе, чем к первому. В частности, нет необходимости создавать строку в конце каждой строки. Компилятор может вести себя так, как вы говорите, но он не обязан это делать.
CodesInChaos
@CodesInChaos, неизменяемая строковая переменная специально назначается в конце каждой строки, разве это не создает обязательство для создания строки? Тем не менее, я согласен с тем, что нет причин по-разному относиться к каждой отдельной строке (и я не уверен, что это так), хотя потеря производительности происходит из-за перераспределения, так что это не имеет значения.
Саеб Амини
@SaebAmini - Если aэто локальная переменная, и объект, на который она ссылается, не был назначен какой-либо другой переменной (которая может быть доступна другому потоку), хороший оптимизатор может определить, aк чему не обращается какой-либо другой код во время этой последовательности линии; только окончательная ценность aимеет значение. Таким образом, он может обрабатывать эти три строки кода, как если бы они были написаны a = b + c + d;.
ToolmakerSteve
29

StringBuilder предпочтительнее, если вы выполняете несколько циклов или разветвлений при передаче кода ... однако, для производительности PURE, если вы можете обойтись без объявления строк SINGLE , тогда это гораздо более производительно.

Например:

string myString = "Some stuff" + var1 + " more stuff"
                  + var2 + " other stuff" .... etc... etc...;

более производительный, чем

StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..

В этом случае StringBuild можно считать более поддерживаемым, но не более производительным, чем однострочное объявление.

9 раз из 10, хотя ... используйте строителя строк.

С другой стороны: строка + var также более производительна, чем подход string.Format (обычно), который использует StringBuilder для внутреннего использования (в случае сомнений ... проверьте отражатель!)

calebjenkins
источник
17
Я хотел бы, чтобы вы сказали, откуда вы это знаете / как это проверить.
ChrisW
2
Вы не проверяете производительность в отражателе: вы проверяете производительность по времени выпуска кода, анализируете с помощью профилировщика и ищите объяснения с помощью отражателя.
Альбин Суннанбо
3
Подумайте об использовании String.Format () для объединения небольшого числа небольших строк, особенно целью является форматирование сообщений для отображения пользователю.
Гари Киндель
1
Это очень плохая информация. («строка myString более производительна») вообще не соответствует действительности.
Том Стиккель
3
Информация в этом ответе неверна. StringBuilder немного быстрее, чем конкатенация, выполненная в том же выражении; однако вы заметите разницу между ними только в том случае, если вы сделаете это сотни тысяч раз ( Источник ). Как сказано в источнике, "это просто не имеет значения!"
Дэвид Шеррет
25

Простой пример, демонстрирующий разницу в скорости при использовании Stringобъединения StringBuilder:

System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
    test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");

Результат:

Использование конкатенации строк: 15423 миллисекунд

StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
    test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");

Результат:

Использование StringBuilder: 10 миллисекунд

В результате первая итерация заняла 15423 мс, а вторая итерация - StringBuilder10 мс.

Мне кажется, что использование StringBuilderбыстрее, намного быстрее.

Diizzy
источник
24

Этот тест показывает, что обычная конкатенация быстрее при объединении 3 или менее строк.

http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/

StringBuilder может значительно улучшить использование памяти, особенно в случае добавления 500 строк вместе.

Рассмотрим следующий пример:

string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
    buffer += i.ToString();
}
return buffer;

Что происходит в памяти? Создаются следующие строки:

1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"

Добавив эти пять чисел в конец строки, мы создали 13 строковых объектов! И 12 из них были бесполезны! Вот Это Да!

StringBuilder решает эту проблему. Это не «изменяемая строка», как мы часто слышим ( все строки в .NET являются неизменяемыми ). Работает, сохраняя внутренний буфер, массив char. Вызов Append () или AppendLine () добавляет строку в пустое пространство в конце массива char; если массив слишком мал, он создает новый, больший массив и копирует туда буфер. Таким образом, в приведенном выше примере StringBuilder может потребоваться только один массив, содержащий все 5 дополнений к строке - в зависимости от размера его буфера. Вы можете указать StringBuilder, насколько большим должен быть его буфер в конструкторе.

Мэтт Труннелл
источник
2
Незначительная причина: немного странно говорить «мы создали 13 строковых объектов, и 12 из них были бесполезны», а затем сказать, что StringBuilder решает эту проблему. В конце концов, шесть строк у вас нет выбора, кроме как создать; они из i.ToString(). Так что с StringBuilder вам все равно нужно создать строки 6 + 1; это уменьшило создание 13 строк до создания 7 строк. Но это все равно неправильный взгляд на это; создание шести числовых строк не имеет значения. Итог: вы просто не должны были упоминать шесть строк, созданных i.ToString(); они не являются частью сравнения эффективности.
ToolmakerSteve
13

String Vs String Builder:

Прежде всего, вы должны знать, в каком собрании живут эти два класса?

Так,

строка присутствует в Systemпространстве имен.

и

StringBuilder присутствует в System.Textпространстве имен.

Для объявления строки :

Вы должны включить Systemпространство имен. что-то вроде этого. Using System;

и

Для объявления StringBuilder :

Вы должны включить System.textпространство имен. что-то вроде этого. Using System.text;

Приходите на актуальный вопрос.

В чем разница между string и StringBuilder ?

Основное различие между этими двумя заключается в том, что:

Строка неизменна.

и

StringBuilder изменчив.

Итак, теперь давайте обсудим разницу между неизменным и изменяемым

Mutable: : средства изменчива.

Неизменный: означает не изменяемый.

Например:

using System;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // String Example

            string name = "Rehan";
            name = name + "Shah";
            name = name + "RS";
            name = name + "---";
            name = name + "I love to write programs.";

            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah RS --- I love to write programs."
        }
    }
}

Так что в этом случае мы собираемся менять один и тот же объект 5 раз.

Итак, очевидный вопрос таков! Что на самом деле происходит под капотом, когда мы меняем одну и ту же струну 5 раз.

Это то, что происходит, когда мы меняем одну и ту же строку 5 раз.

давай посмотрим на фигуру.

введите описание изображения здесь

Explaination:

Когда мы впервые инициализируем эту переменную «name» в «Rehan», то есть string name = "Rehan" эта переменная создается в стеке «name» и указывает на это значение «Rehan». после выполнения этой строки: "name = name +" Shah ". Переменная-ссылка больше не указывает на этот объект" Rehan ", теперь она указывает на" Shah "и так далее.

То stringже самое означает, что как только мы создаем объект в памяти, мы не можем его изменить.

Поэтому, когда мы объединяем nameпеременную, предыдущий объект остается там в памяти, и создается другой новый строковый объект ...

Итак, из приведенного выше рисунка у нас есть пять объектов, четыре объекта выброшены, они вообще не используются. Они все еще остаются в памяти, и они занимают объем памяти. «Сборщик мусора» отвечает за то, что так убирает ресурсы из памяти.

Так что в случае строки в любое время, когда мы манипулируем строкой снова и снова, у нас есть много объектов, созданных и находящихся там в памяти.

Так что это история строковой переменной.

Теперь давайте посмотрим на объект StringBuilder. Например:

using System;
using System.Text;

namespace StringVsStrigBuilder
{
    class Program
    {
        static void Main(string[] args)
        {
            // StringBuilder Example

            StringBuilder name = new StringBuilder();
            name.Append("Rehan");
            name.Append("Shah");
            name.Append("RS");
            name.Append("---");
            name.Append("I love to write programs.");


            // Now when I run this program this output will be look like this.
            // output : "Rehan Shah Rs --- I love to write programs."
        }
    }
}

Так что в этом случае мы собираемся менять один и тот же объект 5 раз.

Итак, очевидный вопрос таков! Что на самом деле происходит под капотом, когда мы меняем один и тот же StringBuilder 5 раз.

Вот что происходит, когда мы меняем один и тот же StringBuilder 5 раз.

давай посмотрим на фигуру. введите описание изображения здесь

Объяснение: В случае объекта StringBuilder . Вы не получите новый объект. Тот же объект будет изменен в памяти, поэтому даже если вы измените объект и, скажем, 10 000 раз, у нас останется только один объект stringBuilder.

У вас нет большого количества мусорных объектов или объектов non_referenced stringBuilder, потому что это может быть изменено. Это изменчивое значение, что оно меняется со временем?

Отличия:

  • String присутствует в пространстве имен System, а Stringbuilder присутствует в пространстве имен System.Text.
  • Строка является неизменной, где StringBuilder является мутабом.
Рехан Шах
источник
12

Да, StringBuilderдает лучшую производительность при выполнении повторной операции над строкой. Это потому, что все изменения вносятся в один экземпляр, поэтому он может сэкономить много времени вместо создания нового экземпляра String.

String Vs Stringbuilder

  • String

    1. под Systemпространством имен
    2. неизменяемый (только для чтения) экземпляр
    3. производительность ухудшается, когда происходит постоянное изменение значения
    4. потокобезопасный
  • StringBuilder (изменяемая строка)

    1. под System.Textпространством имен
    2. изменчивый экземпляр
    3. показывает лучшую производительность, так как новые изменения сделаны в существующем экземпляре

Настоятельно рекомендую статью о дотнет-мобе: String Vs StringBuilder в C # .

Связанный вопрос переполнения стека: изменчивость строки, когда строка не изменяется в C #? ,

Шамсир К
источник
8

StringBuilder уменьшает количество выделений и назначений за счет использования дополнительной памяти. При правильном использовании он может полностью избавить компилятор от необходимости снова и снова выделять все большие и большие строки, пока не будет найден результат.

string result = "";
for(int i = 0; i != N; ++i)
{
   result = result + i.ToString();   // allocates a new string, then assigns it to result, which gets repeated N times
}

против

String result;
StringBuilder sb = new StringBuilder(10000);   // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
   sb.Append(i.ToString());          // fill the buffer, resizing if it overflows the buffer
}

result = sb.ToString();   // assigns once
moswald
источник
5

Производительность операции конкатенации для объекта String или StringBuilder зависит от того, как часто происходит выделение памяти. Операция конкатенации String всегда выделяет память, тогда как операция конкатенации StringBuilder выделяет память, только если буфер объекта StringBuilder слишком мал для размещения новых данных. Следовательно, класс String предпочтителен для операции конкатенации, если конкатенируется фиксированное число объектов String. В этом случае отдельные операции конкатенации могут даже объединяться в одну операцию компилятором. Объект StringBuilder предпочтителен для операции конкатенации, если конкатенируется произвольное количество строк; например, если цикл объединяет случайное количество строк пользовательского ввода.

Источник: MSDN

user487069
источник
3

StringBuilder лучше для построения строки из многих непостоянных значений.

Если вы строите строку из большого количества константных значений, таких как несколько строк значений в документе HTML или XML или другие фрагменты текста, вы можете просто добавить одну и ту же строку, потому что почти все компиляторы делают «постоянное сворачивание», процесс сокращения дерева разбора, когда у вас есть куча постоянных манипуляций (это также используется, когда вы пишете что-то вроде int minutesPerYear = 24 * 365 * 60). А для простых случаев с непостоянными значениями, добавляемыми друг к другу, компилятор .NET уменьшит ваш код до чего-то похожего на то, что StringBuilderделает.

Но когда ваше добавление не может быть уменьшено компилятором до чего-то более простого, вы захотите a StringBuilder. Как указывает fizch, это чаще всего происходит внутри цикла.

JasonTrue
источник
2

Я считаю, что StringBuilder работает быстрее, если у вас есть более 4 строк, которые нужно добавить вместе. Кроме того, он может делать некоторые интересные вещи, такие как AppendLine.

Гиллиган
источник
2

В .NET StringBuilder все еще быстрее, чем добавление строк. Я уверен, что в Java они просто создают StringBuffer под капотом, когда вы добавляете строки, так что на самом деле нет никакой разницы. Я не уверен, почему они еще не сделали этого в .NET.

Эрик З Борода
источник
2

Использование строк для конкатенации может привести к сложности времени выполнения порядка O(n^2).

Если вы используете a StringBuilder, то копирование памяти будет намного меньше. С помощью StringBuilder(int capacity)вы можете увеличить производительность, если сможете оценить, насколько большим будет финал String. Даже если вы не точны, вам, вероятно, придется увеличить пропускную способность всего StringBuilderв пару раз, что также может повысить производительность.

Стив г
источник
2

Я видел значительный прирост производительности при использовании EnsureCapacity(int capacity)вызова метода в экземпляре, StringBuilderпрежде чем использовать его для хранения строк. Я обычно называю это в строке кода после создания экземпляра. Это имеет тот же эффект, как если бы вы создавали StringBuilderподобное:

var sb = new StringBuilder(int capacity);

Этот вызов выделяет необходимую память заблаговременно, что приводит к меньшему выделению памяти во время нескольких Append()операций. Вы должны сделать обоснованное предположение о том, сколько памяти вам понадобится, но для большинства приложений это не должно быть слишком сложным. Я обычно ошибаюсь на стороне слишком большого количества памяти (мы говорим 1k или около того).

Джейсон Джексон
источник
Нет необходимости звонить EnsureCapacityсразу после StringBuilderсоздания экземпляра. Просто создать экземпляр StringBuilderтак: var sb = new StringBuilder(int capacity).
Давид Ференчи Рогожан
Мне сказали, что это помогает использовать простое число.
Карл Гёртсен,
1

В дополнение к предыдущим ответам, первое, что я всегда делаю, когда думаю о таких проблемах, - это создаю небольшое тестовое приложение. Внутри этого приложения проведите некоторый временной тест для обоих сценариев и убедитесь сами, что быстрее.

ИМХО, добавление более 500 строковых записей обязательно должно использовать StringBuilder.

Richs
источник
1

String и StringBuilder на самом деле являются неизменными, StringBuilder имеет встроенные буферы, которые позволяют более эффективно управлять его размером. Когда StringBuilder нужно изменить размер, это когда он перераспределяется в куче. По умолчанию он имеет размер до 16 символов, вы можете установить это в конструкторе.

например.

StringBuilder sb = new StringBuilder (50);

capgpilk
источник
2
Не уверен, что понимаешь, что значит неизменное. Строки неизменны, они НЕ МОГУТ быть изменены. Все, что «изменилось», на самом деле просто то, что старое значение остается в куче без ЛЮБОГО указателя, чтобы найти его. StringBuilder (mutable) является ссылочным типом в куче, указатель на кучу изменяется и выделяется место для этого изменения.
Том Стиккель
1

Конкатенация строк обойдется вам дороже. В Java вы можете использовать либо StringBuffer, либо StringBuilder в зависимости от ваших потребностей. Если вы хотите синхронизированную и поточно-ориентированную реализацию, выберите StringBuffer. Это будет быстрее, чем конкатенация строк.

Если вам не нужна синхронизированная или поточно-ориентированная реализация, выберите StringBuilder. Это будет быстрее, чем конкатенация строк, а также быстрее, чем StringBuffer, так как они не требуют дополнительной синхронизации.

raffimd
источник
0

StringBuilder, вероятно, предпочтительнее. Причина в том, что он выделяет больше места, чем требуется в настоящее время (вы устанавливаете количество символов), чтобы оставить место для будущих добавлений. Тогда те будущие добавления, которые помещаются в текущий буфер, не требуют выделения памяти или сборки мусора, что может быть дорогостоящим. В общем, я использую StringBuilder для сложной конкатенации строк или многократного форматирования, затем преобразовываю в обычную строку, когда данные полны, и я снова хочу неизменный объект.

Димер
источник
0

Если вы делаете много конкатенации строк, используйте StringBuilder. Когда вы объединяете строку, вы каждый раз создаете новую строку, занимая больше памяти.

Alex

Алекс Форт
источник
0

Как общее практическое правило, если мне нужно установить значение строки более одного раза, или если есть какие-либо добавления к строке, то он должен быть строителем строки. Я видел приложения, которые я написал в прошлом, прежде чем узнал о строителях строк, у которых был огромный отпечаток памяти, который, кажется, продолжает расти и расти. Изменение этих программ для использования строителя строк значительно сокращает использование памяти. Теперь я клянусь строителем строк.

user13288
источник
0

StringBuilder значительно эффективнее, но вы не увидите эту производительность, если не будете выполнять большое количество модификаций строк.

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

Как вы можете видеть, 200 000 итераций заняли 22 секунды, в то время как 1 миллион итераций с использованием StringBuilderбыл почти мгновенным.

string s = string.Empty;
StringBuilder sb = new StringBuilder();

Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());

for (int i = 0; i <= 50000; i++)
{
    s = s + 'A';
}

Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();

Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());

for (int i = 0; i <= 200000; i++)
{
    s = s + 'A';
}

Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());

for (int i = 0; i <= 1000000; i++)
{
    sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());

Console.ReadLine();

Результат приведенного выше кода:

Стартовая строка + в 28.01.2013 16:55:40.

Готовая строка + на 28.01.2013 16:55:40.

Стартовая строка + в 28.01.2013 16:55:40.

Готовая строка + на 28.01.2013 16:56:02.

Начало Сб дописать 28.01.2013 16:56:02.

Закончено добавление Сб в 28.01.2013 16:56:02.

CathalMF
источник
1
Если String Builder настолько мощен, то почему у нас есть String. Почему бы нам полностью не уничтожить String. Я задал этот вопрос, чтобы вы могли сказать мне ПРЕИМУЩЕСТВО String по сравнению с StringBuilder
Unbreakable
-2

StringBuilder будет работать лучше, с точки зрения памяти. Что касается обработки, то разница во времени исполнения может быть незначительной.

DevelopingChris
источник