string vs. stringbuilder

<p>я понимаю разницу между <code>string</code> и <code>stringbuilder</code> (<code>stringbuilder</code> является изменяемой), но есть ли большая разница в производительности между ними?</p><p>в программе, над которой я работаю, добавлено много строк (500+). является ли использование <code>stringbuilder</code> лучшим выбором?</p>

<p>да, разница в производительности значительна. см. статью базы знаний " <a href="http://support.microsoft.com/kb/306822">как улучшить производительность конкатенации строк в visual c #</a> ".</p><p>я всегда пытался сначала написать код для ясности, а потом оптимизировать для повышения производительности. это намного проще, чем делать это наоборот! однако, увидев огромную разницу в производительности в моих приложениях между этими двумя, я теперь думаю об этом немного более тщательно.</p><p>к счастью, довольно просто выполнить анализ производительности вашего кода, чтобы увидеть, на что вы тратите время, а затем изменить его, чтобы использовать <code>stringbuilder</code>, где это необходимо.</p>

<p>чтобы уточнить, что сказала джиллиан о 4-й строке, если у вас есть что-то вроде этого:</p><pre><code>string a,b,c,d; a = b + c + d; </code></pre><p>тогда было бы быстрее использовать строки и оператор плюс. это потому, что (как и java, как указывает эрик), он автоматически использует stringbuilder (на самом деле, он использует примитив, который также использует stringbuilder)</p><p>однако, если то, что вы делаете, ближе к:</p><pre><code>string a,b,c,d; a = a + b; a = a + c; a = a + d; </code></pre><p>тогда вам нужно явно использовать stringbuilder. .net здесь не создает автоматически stringbuilder, потому что это было бы бессмысленно. в конце каждой строки "a" должна быть (неизменяемой) строкой, поэтому она должна создавать и размещать stringbuilder в каждой строке. для скорости вам нужно будет использовать тот же stringbuilder, пока вы не закончите сборку:</p><pre><code>string a,b,c,d; stringbuilder e = new stringbuilder(); e.append(b); e.append(c); e.append(d); a = e.tostring(); </code></pre>

<p>stringbuilder предпочтительнее <em>если</em> вы делаете несколько циклов или разветвлений в проходе кода ... однако, для производительности pure, если вы можете обойтись с <em>single</em> = объявление строки, тогда это гораздо более производительно.</p><p>например:</p><pre><code>string mystring = "some stuff" + var1 + " more stuff" + var2 + " other stuff" .... etc... etc...; </code></pre><p>более производительный, чем</p><pre><code>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.. </code></pre><p>в этом случае stringbuild можно считать более поддерживаемым, но не более производительным, чем однострочное объявление.</p><p>9 раз из 10, хотя ... используйте строителя строк.</p><p>с другой стороны: строка + var также более производительна, чем подход string.format (обычно), который использует stringbuilder для внутреннего использования (если есть сомнения ... проверьте отражатель!)</p>

<p>этот тест показывает, что обычная конкатенация быстрее при объединении 3 или менее строк.</p><p> <a href="http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/">http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/</a> </p><p>stringbuilder может значительно улучшить использование памяти, особенно в случае добавления 500 строк вместе.</p><p>рассмотрим следующий пример:</p><pre><code>string buffer = "the numbers are: "; for( int i = 0; i < 5; i++) { buffer += i.tostring(); } return buffer; </code></pre><p>что происходит в памяти? создаются следующие строки:</p><pre><code>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" </code></pre><p>добавив эти пять чисел в конец строки, мы создали 13 строковых объектов! и 12 из них были бесполезны! вот это да!</p><p>stringbuilder решает эту проблему. это не "изменяемая строка", как мы часто слышим (<em> все строки в .net являются неизменяемыми </em>). он работает, сохраняя внутренний буфер, массив char. вызов append () или appendline () добавляет строку в пустое пространство в конце массива char; если массив слишком мал, он создает новый, больший массив и копирует туда буфер. таким образом, в приведенном выше примере stringbuilder может потребоваться только один массив, содержащий все 5 дополнений к строке - в зависимости от размера его буфера. вы можете указать stringbuilder, насколько большим должен быть его буфер в конструкторе.</p>

<p>простой пример, демонстрирующий разницу в скорости при использовании конкатенации <code>string</code> vs <code>stringbuilder</code>:</p><pre><code>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"); </code></pre><p>результат:</p><blockquote> <p>использование конкатенации строк: 15423 миллисекунд</p> </blockquote><pre><code>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"); </code></pre><p>результат:</p><blockquote> <p>использование stringbuilder: 10 миллисекунд</p> </blockquote><p>в результате первая итерация заняла 15423 мс, а вторая итерация с использованием <code>stringbuilder</code> заняла 10 мс.</p><p>мне кажется, что использование <code>stringbuilder</code> быстрее, намного быстрее.</p>

<p>да, <code>stringbuilder</code> дает лучшую производительность при выполнении повторной операции над строкой. это происходит потому, что все изменения вносятся в один экземпляр, поэтому он может сэкономить много времени вместо создания нового экземпляра, такого как <code>string</code>.</p><h3>string vs stringbuilder</h3><ul> <li> <p><code>string</code></p> <ol> <li>в пространстве имен <code>system</code></li> <li>неизменяемый (только для чтения) экземпляр</li> <li>производительность падает, когда происходит постоянное изменение значения</li> <li>потокобезопасный</li> </ol> </li> <li> <p><code>stringbuilder</code> (изменяемая строка)</p> <ol> <li>в пространстве имен <code>system.text</code></li> <li>изменчивый экземпляр</li> <li>показывает лучшую производительность, поскольку в существующий экземпляр вносятся новые изменения</li> </ol> </li> </ul><blockquote> <p>настоятельно рекомендуем статью о дотнет-мобе: <a href="http://codaffection.com/csharp-article/difference-string-and-stringbuilder-in-csharp/">string vs stringbuilder в c #</a> .</p> <p>связанный вопрос переполнения стека: <a href="https://stackoverflow.com/questions/28511598/mutability-of-string-when-string-doesnt-change-in-c/28511709#28511709">изменчивость строки, когда строка не изменяется в c #?</a> .</p> </blockquote>

<p>stringbuilder уменьшает количество выделений и назначений за счет использования дополнительной памяти. при правильном использовании он может полностью избавить компилятор от необходимости снова и снова выделять все большие и большие строки, пока не будет найден результат.</p><pre><code>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 } </code></pre><p>vs.</p><pre><code>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 </code></pre>

<p><strong> string vs string builder: </strong></p><p>прежде всего вы должны знать, в каком собрании живут эти два класса?</p><p>так,</p><p><strong> строка </strong> присутствует в пространстве имен <code>system</code>.</p><p>а также</p><p><strong> stringbuilder </strong> присутствует в пространстве имен <code>system.text</code>.</p><p>для <strong> строкового </strong> объявления:</p><p>вы должны включить пространство имен <code>system</code>. что-то вроде этого. <code>using system;</code></p><p>а также</p><p>для <strong> объявления stringbuilder </strong>:</p><p>вы должны включить пространство имен <code>system.text</code>. что-то вроде этого. <code>using system.text;</code></p><p>приходите на актуальный вопрос.</p><p>в чем разница между <strong> строкой </strong> и <strong> stringbuilder </strong>?</p><p><strong> <em> основное различие между этими двумя заключается в том, что: </em> </strong></p><p><strong> строка </strong> является неизменной.</p><p>а также</p><p><strong> stringbuilder </strong> является изменяемым.</p><p>итак, теперь давайте обсудим разницу между <em> неизменяемым </em> и <em> изменяемым </em></p><p><strong> изменяемый: </strong>: означает изменяемый.</p><p><strong> неизменный: </strong>: означает не изменяемый.</p><p><strong> например: </strong></p><pre><code>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." } } } </code></pre><p>так что в этом случае мы собираемся менять один и тот же объект 5 раз.</p><p>таким образом, очевидный вопрос заключается в том! что на самом деле происходит под капотом, когда мы меняем одну и ту же струну 5 раз.</p><p>это то, что происходит, когда мы меняем одну и ту же строку 5 раз.</p><p>давай посмотрим на фигуру.</p><p> <a href="https://i.stack.imgur.com/q3hap.jpg"><img src="https://i.stack.imgur.com/q3hap.jpg" alt="enter image description here"></a> </p><p><strong> explaination: </strong></p><p>когда мы впервые инициализируем эту переменную "name" как "rehan", i-e <code>string name = "rehan"</code>, эта переменная создается в стеке "name" и указывает на это значение "rehan". после выполнения этой строки: "name = name +" shah ". переменная-ссылка больше не указывает на этот объект" rehan ", теперь она указывает на" shah "и так далее.</p><p>таким образом, <code>string</code> является неизменным, что означает, что как только мы создадим объект в памяти, мы не сможем его изменить.</p><p>поэтому, когда мы объединяем переменную <code>name</code>, предыдущий объект остается там в памяти, и создается другой новый строковый объект ...</p><p>итак, из приведенного выше рисунка у нас есть пять объектов, четыре объекта выброшены, они вообще не используются. они все еще остаются в памяти, и они занимают объем памяти. "сборщик мусора" отвечает за то, что так убирает ресурсы из памяти.</p><p>так что в случае строки в любое время, когда мы манипулируем строкой снова и снова, у нас есть много объектов, созданных и находящихся там в памяти.</p><p>так что это история строковой переменной.</p><p>теперь давайте посмотрим на объект stringbuilder. <strong> например: </strong></p><pre><code>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." } } } </code></pre><p>так что в этом случае мы собираемся менять один и тот же объект 5 раз.</p><p>таким образом, очевидный вопрос заключается в том! что на самом деле происходит под капотом, когда мы меняем один и тот же stringbuilder 5 раз.</p><p>вот что происходит, когда мы меняем один и тот же stringbuilder 5 раз.</p><p>давай посмотрим на фигуру. <a href="https://i.stack.imgur.com/oqtua.jpg"><img src="https://i.stack.imgur.com/oqtua.jpg" alt="enter image description here"></a> </p><p><strong> объяснение: </strong> в случае объекта stringbuilder. вы не получите новый объект. тот же объект будет изменен в памяти, поэтому даже если вы измените объект и, скажем, 10 000 раз, у нас останется только один объект stringbuilder.</p><p>у вас нет много мусорных объектов или объектов без\_объектов stringbuilder, потому что это может быть изменено. это изменчивое значение, что оно меняется со временем?</p><p><strong> отличия: </strong></p><ul> <li>string присутствует в пространстве имен system, а stringbuilder присутствует в пространстве имен system.text.</li> <li>строка является неизменной, где stringbuilder является мутабом.</li> </ul>

<blockquote> <p>производительность операции конкатенации для объекта string или stringbuilder зависит от того, как часто происходит выделение памяти. операция конкатенации string всегда выделяет память, тогда как операция конкатенации stringbuilder выделяет память, только если буфер объекта stringbuilder слишком мал для размещения новых данных. следовательно, класс string предпочтителен для операции конкатенации, если конкатенируется фиксированное количество объектов string. в этом случае отдельные операции конкатенации могут даже объединяться в одну операцию компилятором. объект stringbuilder предпочтителен для операции конкатенации, если конкатенируется произвольное количество строк; например, если цикл объединяет случайное количество строк пользовательского ввода.</p> </blockquote><p>источник: <a href="http://msdn.microsoft.com/en-us/library/system.text.stringbuilder%28v=vs.90%29.aspx">msdn</a> </p>

<p><code>stringbuilder</code> лучше для построения строки из множества непостоянных значений.</p><p>если вы строите строку из большого количества константных значений, таких как несколько строк значений в документе html или xml или других кусках текста, вы можете просто добавить одну и ту же строку, потому что почти все компиляторы делают "постоянное сворачивание" - процесс сокращения дерева разбора, когда у вас есть куча постоянных манипуляций (он также используется, когда вы пишете что-то вроде <code>int minutesperyear = 24 * 365 * 60</code>). а для простых случаев с непостоянными значениями, добавляемыми друг к другу, компилятор .net уменьшит ваш код до чего-то похожего на то, что делает <code>stringbuilder</code>.</p><p>но когда ваше приложение не может быть уменьшено компилятором до чего-то более простого, вам понадобится <code>stringbuilder</code>. как указывает fizch, это чаще всего происходит внутри цикла.</p>

<p>рассмотрим ' <a href="http://www.codinghorror.com/blog/2009/01/the-sad-tragedy-of-micro-optimization-theater.html">печальная трагедия театра микрооптимизации</a> '.</p>

<p>использование строк для объединения может привести к усложнению во время выполнения порядка <code>o(n^2)</code>.</p><p>если вы используете <code>stringbuilder</code>, копирование памяти требует гораздо меньше усилий. с <code>stringbuilder(int capacity)</code> вы можете повысить производительность, если сможете оценить, насколько большим будет итоговое <code>string</code>. даже если вы не точны, вам, вероятно, придется увеличить емкость <code>stringbuilder</code> только пару раз, что также может повысить производительность.</p>

<p>я видел значительный прирост производительности при использовании вызова метода <code>ensurecapacity(int capacity)</code> для экземпляра <code>stringbuilder</code> перед его использованием для любого хранения строк. я обычно называю это в строке кода после создания экземпляра. это имеет тот же эффект, как если бы вы создали экземпляр <code>stringbuilder</code> следующим образом:</p><pre><code>var sb = new stringbuilder(int capacity); </code></pre><p>этот вызов заблаговременно выделяет необходимую память, что приводит к меньшему выделению памяти во время нескольких операций <code>append()</code>. вы должны сделать обоснованное предположение о том, сколько памяти вам понадобится, но для большинства приложений это не должно быть слишком сложным. я обычно ошибаюсь на стороне слишком большого количества памяти (мы говорим 1k или около того).</p>

<p>я считаю, что stringbuilder быстрее, если у вас есть более 4 строк, которые нужно добавить вместе. плюс он может делать некоторые классные вещи, такие как appendline.</p>

<p>в .net stringbuilder все еще быстрее, чем добавление строк. я почти уверен, что в java они просто создают stringbuffer под капотом, когда вы добавляете строки, так что на самом деле нет никакой разницы. я не уверен, почему они еще не сделали этого в .net.</p>

<p>string и stringbuilder на самом деле являются неизменными, stringbuilder имеет встроенные буферы, которые позволяют более эффективно управлять его размером. когда stringbuilder необходимо изменить размер, это когда он перераспределяется в куче. по умолчанию он имеет размер до 16 символов, вы можете установить это в конструкторе.</p><p>например.</p><p>stringbuilder sb = new stringbuilder (50);</p>

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

<p>конкатенация строк обойдется вам дороже. в java вы можете использовать либо stringbuffer, либо stringbuilder в зависимости от ваших потребностей. если вы хотите синхронизированную и поточно-ориентированную реализацию, перейдите к stringbuffer. это будет быстрее, чем конкатенация строк.</p><p>если вам не нужна синхронизированная или поточно-ориентированная реализация, перейдите на stringbuilder. это будет быстрее, чем конкатенация строк, а также быстрее, чем stringbuffer, так как они не требуют дополнительной синхронизации.</p>

<p><code>stringbuilder</code> значительно более эффективен, но вы не увидите эту производительность, если не будете выполнять большое количество модификаций строк.</p><p>ниже приведен небольшой фрагмент кода, чтобы привести пример производительности. как вы можете видеть, вы действительно начинаете видеть значительное увеличение производительности, когда начинаете большие итерации.</p><p>как вы можете видеть, 200 000 итераций заняли 22 секунды, в то время как 1 миллион итераций с использованием <code>stringbuilder</code> был почти мгновенным.</p><pre><code>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(); </code></pre><p>результат приведенного выше кода:</p><blockquote> <p>начало строки + 28.01.2013 16:55:40.</p> <p>готовая строка + на 28.01.2013 16:55:40.</p> <p>начало строки + 28.01.2013 16:55:40.</p> <p>готовая строка + на 28.01.2013 16:56:02.</p> <p>начало sb append 28.01.2013 16:56:02.</p> <p>закончено добавление сб в 28.01.2013 16:56:02.</p> </blockquote>

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

<p>мой подход всегда заключался в использовании stringbuilder при конкатенации 4 или более строк or когда я не знаю, как могут происходить конкатенации.</p><p> <a href="http://www.heikniemi.net/hc/archives/000124.html">хорошая статья о производительности здесь</a> </p>

<p>stringbuilder, вероятно, предпочтительнее. причина в том, что он выделяет больше места, чем требуется в настоящее время (вы устанавливаете количество символов), чтобы оставить место для будущих добавлений. тогда те будущие добавления, которые помещаются в текущий буфер, не требуют выделения памяти или сборки мусора, что может быть дорогостоящим. в общем, я использую stringbuilder для сложной конкатенации строк или многократного форматирования, затем преобразовываю в обычную строку, когда данные полны, и я снова хочу неизменный объект.</p>

<p>если вы делаете много конкатенации строк, используйте stringbuilder. когда вы объединяете строку, вы каждый раз создаете новую строку, используя больше памяти.</p><p>alex</p>