String vs. StringBuilder


215

Я розумію різницю між ( Stringі є змінною), але чи є велика різниця між ними?StringBuilderStringBuilder

У програмі, над якою я працюю, є багато доданих рядкових рядків (500+). Чи використовуєте StringBuilderкращий вибір?

Відповіді:


236

Так, різниця в продуктивності значна. Дивіться статтю KB " Як поліпшити продуктивність конкатенації рядків у Visual C # ".

Я завжди намагався спершу кодувати чіткість, а потім оптимізувати її для виконання. Це набагато простіше, ніж робити це навпаки! Однак, побачивши величезну різницю в моїх програмах між двома, тепер я про це продумаю трохи ретельніше.

На щастя, порівняно просто запустити аналіз продуктивності свого коду, щоб побачити, де ви витрачаєте час, а потім змінити його, щоб використовувати, StringBuilderде потрібно.


44
Хорошим правилом є використання рядків, коли ви не збираєтесь його змінювати, і використовувати StringBuilder, якщо ви будете змінювати його.
Тим

31
Мені дуже подобається ця відповідь, особливо рада кодувати для чіткості перед виконанням. Як розробники ми витрачаємо стільки чи більше часу на читання коду, наскільки ми пишемо його.
Скотт Лоуренс

Захист: Я думаю, що це має стати власною відповіддю, про яку голосують окремо, якщо я правильно розумію StackOverflow.
Джей Базузі

2
виходячи з мого досвіду, якщо ви намагаєтеся згорнути близько 10-15 рядків, тоді ви можете перейти з рядком, але якщо ні. струн більше, ніж тоді, використовуйте
конструктор

3
Це все ще залежить від того, як хто його використовує, для посилання на фактично тести, які мені подобається Кодування Хоррор - Сумна трагедія театру
мікрооптимізації

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 продуктивності, якщо ви можете піти з декларації СІНГЛИННОГО рядка, то це набагато ефективніше.

Наприклад:

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, хоча ... використовуйте конструктор струн.

Зі сторони примітки: string + var також більш ефективний, ніж string.Format підхід (як правило), який використовує StringBuilder внутрішньо (коли сумніваєтесь ... перевірити рефлектор!)


17
Я хотів би, щоб ви сказали, як ви це знаєте / як це підтвердити.
ChrisW

2
Ви не перевіряєте продуктивність у рефлекторі. Ви перевіряєте продуктивність за допомогою коду часу випуску, аналізуєте за допомогою профайлера та шукаєте пояснення за допомогою рефлектора.
Альбін Суннанбо

3
Подумайте про використання String.Format () для об'єднання невеликої кількості невеликих рядків, особливо метою є форматування повідомлень для показу користувачеві.
Гері Кіндель

1
Це дуже погана інформація. ("string myString є ефективнішим") зовсім не відповідає дійсності.
Том Стікель

3
Інформація у цій відповіді невірна. StringBuilder трохи швидше, ніж конкатенація, зроблена в тому самому операторі; однак різницю між ними ви помітите, лише якщо зробите це сотні тисяч разів ( Джерело ). Як сказано в джерелі, "це просто не має значення!"
Девід Шеррет

25

Простий приклад продемонструвати різницю швидкостей при використанні Stringконкатенації vs 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швидше, набагато швидше.


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 незмінні ). Він працює, зберігаючи внутрішній буфер, масив знаків. Calling Append () або AppendLine () додає рядок до порожнього простору в кінці масиву char; якщо масив занадто малий, він створює новий, більший масив і копіює там буфер. Так, у наведеному вище прикладі StringBuilder може знадобитися лише один масив, який містить усі 5 доповнень до рядка - залежно від розміру його буфера. Ви можете сказати StringBuilder, наскільки великим повинен бути його буфер у конструкторі.


2
Незначна нитка: трохи дивно сказати, "ми створили 13 рядкових об'єктів, і 12 з них були марними", а потім сказати, StringBuilder виправляє цю проблему. Зрештою, шість струн у вас немає іншого вибору, як створити; вони з i.ToString(). Тож із StringBuilder вам доведеться створити 6 + 1 рядки; це зменшило створення 13 рядків до створення 7 рядків. Але це все-таки неправильний погляд на це; створення шести рядків чисел не має значення. Підсумок: ви просто не повинні були згадувати шість рядків, створених компанією i.ToString(); вони не є частиною порівняння ефективності.
ToolmakerSteve

12

Так, StringBuilderдає кращі показники під час виконання повторних операцій над рядком. Це тому, що всі зміни вносяться в один екземпляр, тому це може заощадити багато часу замість створення нового екземпляра на кшталт String.

String Vs Stringbuilder

  • String

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

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

Настійно рекомендую статтю dotnet mob: String Vs StringBuilder в C # .

Пов'язане запитання про переповнення стека: Переміщення рядка, коли рядок не змінюється в C #? .


12

String Vs String Builder:

Перше, що ви повинні знати, в якій збірці живуть ці два класи?

Так,

рядок присутній у Systemпросторі імен.

і

StringBuilder присутній у System.Textпросторі імен.

Для рядкового оголошення:

Ви повинні включити Systemпростір імен. щось на зразок цього. Using System;

і

Для декларації StringBuilder :

Ви повинні включити System.textпростір імен. щось на зразок цього. Using System.text;

Тепер прийде власне питання.

Що таке Differene між рядком і 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 разів.

нехай подивиться на фігуру.

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

Пояснення:

Коли ми вперше ініціалізуємо цю змінну "name" на "Rehan", тобто string name = "Rehan" ця змінна створюється на стеці "name" і вказує на це значення "Rehan". після виконання цього рядка: "name = name +" Shah ". Довідкова змінна більше не вказує на цей об'єкт" Rehan ", вона тепер вказує на" Shah "тощо.

Це stringнезмінне значення, що коли ми створюємо об'єкт у пам'яті, ми не можемо їх змінити.

Отже, коли ми конкретизуємо nameзмінну, попередній об’єкт залишається в пам'яті, і створюється ще один новий рядковий об'єкт ...

Отже, з наведеної цифри у нас є п'ять об'єктів, чотири об'єкти викидаються, вони взагалі не використовуються. Вони залишаються в пам'яті, і вони займають об'єм пам'яті. "Збір сміття" несе відповідальність за те, що настільки очищає ці ресурси з пам'яті.

Отже, у випадку, якщо в будь-який час ми обробляємо рядок знову і знову, у нас є декілька створених об'єктів і залишаються там у пам'яті.

Отже, це історія змінної струни.

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

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. Ви б не отримали новий об’єкт. Той самий об’єкт буде змінено в пам'яті, тому навіть якщо ви зміните об'єкт і скажете 10000 разів, у нас все одно буде лише один об'єкт stringBuilder.

У вас немає багато сміттєвих об’єктів або об'єктів non_referenced stringBuilder, тому що це може бути змінено. Це змінне значення, яке воно змінюється з часом?

Відмінності:

  • String присутній у просторі імен системи, де Stringbuilder присутній у просторі імен System.Text.
  • рядок є непорушною там, де як StringBuilder є mutabe.

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
}

vs.

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

5

Виконання операції об'єднання для об'єкта String або StringBuilder залежить від того, як часто відбувається розподіл пам'яті. Операція конкатенації рядків завжди виділяє пам'ять, тоді як операція конкатенації StringBuilder виділяє пам'ять лише в тому випадку, якщо буфер об'єктів StringBuilder занадто малий для розміщення нових даних. Отже, клас String є кращим для операції конкатенації, якщо фіксована кількість об'єктів String є об'єднаною. У цьому випадку окремі операції конкатенації можуть навіть поєднуватись у одну операцію компілятором. Об'єкт StringBuilder є кращим для операції конкатенації, якщо довільне число рядків об'єднано; наприклад, якщо цикл об'єднує випадкову кількість рядків введення користувача.

Джерело: MSDN


3

StringBuilder краще для створення рядка з багатьох нестабільних значень.

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

Але коли ваш додаток не може бути зведений компілятором до чогось більш простого, вам потрібно буде StringBuilder. Як зазначає Фізч, це швидше за все відбудеться всередині циклу.


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()операцій. Ви повинні навчитись здогадуватися про те, скільки пам'яті вам знадобиться, але для більшості застосувань це не повинно бути надто складно. Зазвичай я помиляюся на стороні трохи занадто великої пам’яті (ми говоримо 1 к або близько того).


Немає необхідності дзвонити EnsureCapacityодразу після встановлення StringBuilderінстанції. Просто створити екземпляр StringBuilderтак: var sb = new StringBuilder(int capacity).
David Ferenczy Rogožan

Мені сказали, що це допомагає використовувати просте число.
Карл Г'єрцен

1

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

IMHO, для додавання 500+ рядкових записів обов'язково слід використовувати StringBuilder.


1

String і StringBuilder насправді є незмінними, StringBuilder має вбудовані буфери, які дозволяють більш ефективно керувати його розмірами. Коли StringBuilder потребує зміни розміру, це коли він перерозподіляється на купу. За замовчуванням він розміром до 16 символів, ви можете встановити це в конструкторі.

напр.

StringBuilder sb = новий StringBuilder (50);


2
Не впевнений, що ви розумієте, що означає незмінне. Рядки незмінні, їх НЕ МОЖЕ бути змінено. Все, що "змінилося" - це насправді лише те, що старе значення залишається на купі без будь-якого вказівника, щоб знайти його. StringBuilder (змінний) - це тип посилань на купі, вказівник на купу змінюється і виділяється простір для цієї зміни.
Том Стікел

1

Зв'язок рядків обійдеться вам дорожче. У Java ви можете використовувати або StringBuffer, або StringBuilder залежно від ваших потреб. Якщо ви хочете синхронізованої та потокової безпечної реалізації, перейдіть до StringBuffer. Це буде швидше, ніж зв'язок String.

Якщо вам не потрібна синхронізована чи безпечна реалізація теми, перейдіть до StringBuilder. Це буде швидше, ніж об'єднання рядків, а також швидше, ніж StringBuffer, оскільки їхня синхронізація не накладні.


0

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


0

Якщо ви робите багато об'єднання рядків, використовуйте StringBuilder. Коли ви з'єднуєтеся з String, ви створюєте нову String кожен раз, використовуючи більше пам'яті.

Олексій


0

Як правило, якщо мені доведеться встановлювати значення рядка більше одного разу або якщо до нього додаються будь-які додатки, то він повинен бути конструктором рядків. Я бачив додатки, про які писав у минулому, перш ніж дізнатися про будівельників струн, які мали величезний друк на ногах пам'яті, який, здається, постійно зростає та зростає. Зміна цих програм на використання конструктора струн значно зменшила використання пам'яті. Зараз я клянусь строковиком.



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();

Результат наведеного вище коду:

Початок String + в 28.01.2013 16:55:40.

Закінчено String + в 28.01.2013 16:55:40.

Початок String + в 28.01.2013 16:55:40.

Закінчено String + в 28.01.2013 16:56:02.

Початок додавання Sb в 28.01.2013 16:56:02.

Завершено додати в Sb 28.01.2013 16:56:02.


1
Якщо String builder настільки потужний, то чому у нас існує String. Чому б ми повністю не стерти String? Я задав це питання, щоб ви могли сказати мені ПЕРЕВАГУ струни над StringBuilder
Незламний

-2

StringBuilder працюватиме краще, з точки зору стояння пам'яті. Що стосується обробки, різниця у часі виконання може бути незначною.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.