Відповіді:
Так, різниця в продуктивності значна. Дивіться статтю KB " Як поліпшити продуктивність конкатенації рядків у Visual C # ".
Я завжди намагався спершу кодувати чіткість, а потім оптимізувати її для виконання. Це набагато простіше, ніж робити це навпаки! Однак, побачивши величезну різницю в моїх програмах між двома, тепер я про це продумаю трохи ретельніше.
На щастя, порівняно просто запустити аналіз продуктивності свого коду, щоб побачити, де ви витрачаєте час, а потім змінити його, щоб використовувати, StringBuilder
де потрібно.
Щоб уточнити, що Джилліан сказала про 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();
a
це локальна змінна, а об'єкт, на який вона посилається, не був призначений якійсь іншій змінній (яка може бути доступна іншій потоці), хороший оптимізатор може визначити, що a
не має доступу до будь-якого іншого коду під час цієї послідовності лінії; тільки остаточне значення a
питань. Тож вона могла б поводитися з цими трьома рядками коду так, ніби вони були написані a = b + c + d;
.
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 внутрішньо (коли сумніваєтесь ... перевірити рефлектор!)
Простий приклад продемонструвати різницю швидкостей при використанні 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 мс, а друга ітерація - StringBuilder
10 мс.
Мені здається, що використання StringBuilder
швидше, набагато швидше.
Цей орієнтир показує, що регулярне конкатенація швидше при поєднанні 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, наскільки великим повинен бути його буфер у конструкторі.
i.ToString()
. Тож із StringBuilder вам доведеться створити 6 + 1 рядки; це зменшило створення 13 рядків до створення 7 рядків. Але це все-таки неправильний погляд на це; створення шести рядків чисел не має значення. Підсумок: ви просто не повинні були згадувати шість рядків, створених компанією i.ToString()
; вони не є частиною порівняння ефективності.
Так, StringBuilder
дає кращі показники під час виконання повторних операцій над рядком. Це тому, що всі зміни вносяться в один екземпляр, тому це може заощадити багато часу замість створення нового екземпляра на кшталт String
.
String
System
простором іменStringBuilder
(змінна рядок)
System.Text
простором іменНастійно рекомендую статтю dotnet mob: String Vs StringBuilder в C # .
Пов'язане запитання про переповнення стека: Переміщення рядка, коли рядок не змінюється в C #? .
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, тому що це може бути змінено. Це змінне значення, яке воно змінюється з часом?
Відмінності:
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
Виконання операції об'єднання для об'єкта String або StringBuilder залежить від того, як часто відбувається розподіл пам'яті. Операція конкатенації рядків завжди виділяє пам'ять, тоді як операція конкатенації StringBuilder виділяє пам'ять лише в тому випадку, якщо буфер об'єктів StringBuilder занадто малий для розміщення нових даних. Отже, клас String є кращим для операції конкатенації, якщо фіксована кількість об'єктів String є об'єднаною. У цьому випадку окремі операції конкатенації можуть навіть поєднуватись у одну операцію компілятором. Об'єкт StringBuilder є кращим для операції конкатенації, якщо довільне число рядків об'єднано; наприклад, якщо цикл об'єднує випадкову кількість рядків введення користувача.
Джерело: MSDN
StringBuilder
краще для створення рядка з багатьох нестабільних значень.
Якщо ви створюєте рядок з безлічі постійних значень, таких як кілька рядків значень у HTML або XML-документі або інших фрагментах тексту, ви можете уникнути, просто додавши до цього ж рядка, тому що майже всі компілятори роблять "постійне складання", процес зменшення дерева розбору, коли у вас є маса постійних маніпуляцій (він також використовується, коли ви пишете щось на зразок int minutesPerYear = 24 * 365 * 60
). А для простих випадків з непостійними значеннями, доданими один до одного, компілятор .NET зменшить ваш код на щось подібне до того, що StringBuilder
робить.
Але коли ваш додаток не може бути зведений компілятором до чогось більш простого, вам потрібно буде StringBuilder
. Як зазначає Фізч, це швидше за все відбудеться всередині циклу.
У .NET, StringBuilder все ще швидше, ніж додавання рядків. Я впевнений, що в Java вони просто створюють StringBuffer під кришкою, коли ви додаєте рядки, тому різниці немає. Я не впевнений, чому вони ще цього не робили в .NET
Розглянемо « Сумна трагедія театру мікрооптимізації ».
Використання рядків для конкатенації може призвести до складності виконання на порядок O(n^2)
.
Якщо ви користуєтесь a StringBuilder
, копіювання пам'яті набагато менше, що потрібно зробити. За допомогою StringBuilder(int capacity)
ви можете збільшити продуктивність, якщо зможете оцінити, наскільки великим буде фінал String
. Навіть якщо ви не точні, вам, мабуть, доведеться лише StringBuilder
кілька разів збільшити потужність, що також може сприяти продуктивності.
Я побачив значні переваги від використання EnsureCapacity(int capacity)
методу виклику в екземплярі, StringBuilder
перш ніж використовувати його для будь-якого зберігання рядків. Зазвичай я називаю це у рядку коду після інстанції. Це має такий же ефект, як якщо б ви створили StringBuilder
подібне:
var sb = new StringBuilder(int capacity);
Цей виклик виділяє потрібну пам'ять достроково, що призводить до меншої кількості пам'яті під час декількох Append()
операцій. Ви повинні навчитись здогадуватися про те, скільки пам'яті вам знадобиться, але для більшості застосувань це не повинно бути надто складно. Зазвичай я помиляюся на стороні трохи занадто великої пам’яті (ми говоримо 1 к або близько того).
EnsureCapacity
одразу після встановлення StringBuilder
інстанції. Просто створити екземпляр StringBuilder
так: var sb = new StringBuilder(int capacity)
.
На додаток до попередніх відповідей, перше, що я завжди роблю, коли думаю про такі питання, як створити невелику тестову програму. Всередині цього додатка виконайте тест на терміни для обох сценаріїв і переконайтеся, що швидше.
IMHO, для додавання 500+ рядкових записів обов'язково слід використовувати StringBuilder.
String і StringBuilder насправді є незмінними, StringBuilder має вбудовані буфери, які дозволяють більш ефективно керувати його розмірами. Коли StringBuilder потребує зміни розміру, це коли він перерозподіляється на купу. За замовчуванням він розміром до 16 символів, ви можете встановити це в конструкторі.
напр.
StringBuilder sb = новий StringBuilder (50);
Зв'язок рядків обійдеться вам дорожче. У Java ви можете використовувати або StringBuffer, або StringBuilder залежно від ваших потреб. Якщо ви хочете синхронізованої та потокової безпечної реалізації, перейдіть до StringBuffer. Це буде швидше, ніж зв'язок String.
Якщо вам не потрібна синхронізована чи безпечна реалізація теми, перейдіть до StringBuilder. Це буде швидше, ніж об'єднання рядків, а також швидше, ніж StringBuffer, оскільки їхня синхронізація не накладні.
StringBuilder, мабуть, кращий. Причина полягає в тому, що він виділяє більше місця, ніж зараз потрібно (ви встановлюєте кількість символів), щоб залишити місце для подальших додатків. Тоді ті майбутні додатки, які вміщуються у поточному буфері, не потребують виділення пам'яті чи збирання сміття, що може дорого коштувати. Як правило, я використовую StringBuilder для складного контенту рядків або багаторазового форматування, а потім перетворюю на звичайний String, коли дані завершені, і я хочу знову непорушний об'єкт.
Якщо ви робите багато об'єднання рядків, використовуйте StringBuilder. Коли ви з'єднуєтеся з String, ви створюєте нову String кожен раз, використовуючи більше пам'яті.
Олексій
Як правило, якщо мені доведеться встановлювати значення рядка більше одного разу або якщо до нього додаються будь-які додатки, то він повинен бути конструктором рядків. Я бачив додатки, про які писав у минулому, перш ніж дізнатися про будівельників струн, які мали величезний друк на ногах пам'яті, який, здається, постійно зростає та зростає. Зміна цих програм на використання конструктора струн значно зменшила використання пам'яті. Зараз я клянусь строковиком.
Мій підхід завжди полягав у використанні StringBuilder при об'єднанні 4 або більше рядків АБО Коли я не знаю, як можуть відбуватися конкатекації.
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.
StringBuilder працюватиме краще, з точки зору стояння пам'яті. Що стосується обробки, різниця у часі виконання може бути незначною.