зачем использовать stringbuffer в java вместо оператора конкатенации строк

<p>кто-то сказал мне, что более эффективно использовать <code>stringbuffer</code> для объединения строк в java, чем использовать оператор <code>+</code> для <code>string</code>s. что происходит под капотом, когда вы делаете это? что <code>stringbuffer</code> делает по-другому?</p>

<p>лучше использовать stringbuilder (это несинхронизированная версия; когда вы строите строки параллельно?) в наши дни, почти в каждом случае, но вот что происходит:</p><p>когда вы используете + с двумя строками, он компилирует код следующим образом:</p><pre><code>string third = first + second; </code></pre><p>чтобы что-то вроде этого:</p><pre><code>stringbuilder builder = new stringbuilder( first ); builder.append( second ); third = builder.tostring(); </code></pre><p>поэтому для небольших примеров это обычно не имеет значения. но когда вы строите сложную строку, вам часто приходится иметь дело с гораздо большим, чем это; например, вы можете использовать много разных добавляемых операторов или цикл вроде этого:</p><pre><code>for( string str : strings ) { out += str; } </code></pre><p>в этом случае новый экземпляр <code>stringbuilder</code> и новое имя <code>string</code> (новое значение <code>out</code> - <code>string</code>s являются неизменяемыми) требуются в каждой итерации. это очень расточительно. замена этого на одно <code>stringbuilder</code> означает, что вы можете просто создать одно <code>string</code> и не заполнять кучу <code>string</code>s, которые вас не интересуют.</p>

<p>для простых объединений, таких как:</p><pre><code>string s = "a" + "b" + "c"; </code></pre><p>довольно бессмысленно использовать <code>stringbuffer</code> - как <em> jodonnell </em> указал, что он будет умно переведен на:</p><pre><code>string s = new stringbuffer().append("a").append("b").append("c").tostring(); </code></pre><p><strong>но</strong> конкатенация строк в цикле очень бесполезна, например:</p><pre><code>string s = ""; for (int i = 0; i < 10; i++) { s = s + integer.tostring(i); } </code></pre><p>использование строки в этом цикле сгенерирует 10 промежуточных строковых объектов в памяти: "0", "01", "012" и так далее. при написании того же с использованием <code>stringbuffer</code> вы просто обновляете некоторый внутренний буфер <code>stringbuffer</code> и не создаете те промежуточные строковые объекты, которые вам не нужны:</p><pre><code>stringbuffer sb = new stringbuffer(); for (int i = 0; i < 10; i++) { sb.append(i); } </code></pre><p>на самом деле для приведенного выше примера вы должны использовать <code>stringbuilder</code> (введено в java 1.5) вместо <code>stringbuffer</code> - <code>stringbuffer</code> немного тяжелее, так как все его методы синхронизированы.</p>

<p>один не должен быть быстрее другого. это не было правдой до java 1.4.2, потому что при объединении более двух строк с помощью оператора "+" промежуточные объекты <code>string</code> будут создаваться в процессе построения окончательной строки.</p><p>однако, как говорится в <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/stringbuffer.html">javadoc for stringbuffer</a> , по крайней мере, поскольку java 1.4.2 с использованием оператора "+" компилируется до создания <code>stringbuffer</code> и <code>append()</code> из множества строк. так что нет разницы, по-видимому.</p><p>однако будьте осторожны при использовании добавления строки в другую внутри цикла! например:</p><pre><code>string mystring = ""; for (string s : listofstrings) { // be careful! you're creating one intermediate string object // for every iteration on the list (this is costly!) mystring += s; } </code></pre><p>имейте в виду, однако, что обычно объединение нескольких строк с "+" чище, чем <code>append()</code>ing их всех.</p>

<p>под капотом он фактически создает и добавляет к stringbuffer, вызывая tostring () для результата. так что на самом деле не имеет значения, какой вы используете больше.</p><p>так</p><pre><code>string s = "a" + "b" + "c"; </code></pre><p>становится</p><pre><code>string s = new stringbuffer().append("a").append("b").append("c").tostring(); </code></pre><p>это верно для нескольких встроенных добавлений в одном выражении. если вы строите свою строку в течение нескольких операторов, то вы тратите впустую память, и stringbuffer или stringbuilder - ваш лучший выбор.</p>

<p>я думаю, что с учетом jdk1.5 (или выше) и вашей конкатенации потокобезопасен, вы должны использовать stringbuilder вместо stringbuffer <a href="http://java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs-stringbuilder.html">http://java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs -stringbuilder.html</a> что касается увеличения скорости: <a href="http://www.about280.com/stringtest.html">http://www.about280.com/stringtest.html</a> </p><p>лично я бы написал код для читабельности, поэтому, если вы не обнаружите, что конкатенация строк делает ваш код значительно медленнее, используйте любой метод, который сделает ваш код более читабельным.</p>

<p>в некоторых случаях это устарело из-за оптимизаций, выполняемых компилятором, но общая проблема заключается в следующем:</p><pre><code>string mystring=""; for(int i=0;i<x;i++) { mystring += "x"; } </code></pre><p>будет действовать как показано ниже (каждый шаг является следующей итерацией цикла):</p><ol> <li>построить строковый объект длиной 1 и значением "x"</li> <li>создайте новый строковый объект размером 2, скопируйте в него старую строку "x", добавьте "x" в позицию 2.</li> <li>создайте новый строковый объект размером 3, скопируйте в него старую строку "xx", добавьте "x" в позицию 3.</li> <li>... и так далее</li> </ol><p>как видите, на каждой итерации приходится копировать еще один символ, в результате чего мы выполняем 1 + 2 + 3 + 4 + 5 + ... + n операций в каждом цикле. это операция o (n ^ 2). однако, если бы мы заранее знали, что нам нужно только n символов, мы могли бы сделать это за один раз, скопировав всего n символов из используемых нами строк - простая операция o(n).</p><p>stringbuffer/stringbuilder избегают этого, потому что они изменчивы, и поэтому не нужно копировать одни и те же данные снова и снова (до тех пор, пока в их внутреннем буфере есть место для копирования). они избегают выполнения выделения и копирования пропорционально количеству добавлений, выполненных путем чрезмерного выделения их буфера пропорционально его текущему размеру, давая добавление амортизированного o(1).</p><p>однако стоит отметить, что часто компилятор может автоматически оптимизировать код в стиле stringbuilder (или лучше - поскольку он может выполнять постоянное свертывание и т.д.).</p>

<p>afaik это зависит от версии jvm, в версиях до 1.5 с использованием "+" или "+ =" фактически каждый раз копируется вся строка.</p><p>помните, что использование + = фактически выделяет новую копию строки.</p><p>как было указано, использование + в циклах подразумевает копирование.</p><p>когда строки, которые являются конкатенацией, являются константами времени компиляции, они объединяются во время компиляции, поэтому</p><pre><code>string foo = "a" + "b" + "c"; </code></pre><p>имеет компиляцию для:</p><pre><code>string foo = "abc"; </code></pre>

<p>java превращает string1 + string2 в конструкцию stringbuffer, append () и tostring (). это имеет смысл.</p><p>однако в java 1.4 и более ранних версиях это будет сделано для оператора <em>каждого</em> + в выражении <em>отдельно</em>. это означало, что выполнение a + b + c приведет к <em>двум</em> конструкциям stringbuffer с <em>двумя</em> вызовами tostring (). если бы у вас была длинная цепочка конкататов, это превратилось бы в настоящий беспорядок. делать это самостоятельно означало, что вы можете контролировать это и делать это правильно.</p><p>java 5.0 и выше, кажется, делают это более разумно, так что это меньше проблем и, конечно, менее многословно.</p>

<p>дальнейшая информация:</p><p>stringbuffer - это потокобезопасный класс</p><pre><code> public final class stringbuffer extends abstractstringbuilder implements serializable, charsequence { // .. skip .. public synchronized stringbuffer append(stringbuffer stringbuffer) { super.append(stringbuffer); return this; } // .. skip .. } </code></pre><p>но stringbuilder не является поточно-ориентированным, поэтому по возможности быстрее использовать stringbuilder</p><pre><code> public final class stringbuilder extends abstractstringbuilder implements serializable, charsequence { // .. skip .. public stringbuilder append(string s) { super.append(s); return this; } // .. skip .. } </code></pre>

<p>stringbuffer является изменяемым. он добавляет значение строки к объекту <em>same</em> без создания экземпляра другого объекта. делать что-то вроде:</p><pre><code>mystring = mystring + "xyz" </code></pre><p>создаст <em>новый</em> объект string.</p>

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

<p>класс stringbuffer поддерживает массив символов для хранения содержимого строк, которые вы объединяете, тогда как метод + создает новую строку каждый раз при ее вызове и добавляет два параметра (param1 + param2).</p><p>stringbuffer работает быстрее, потому что 1. он может использовать уже существующий массив для объединения/хранения всех строк. 2. даже если они не помещаются в массив, быстрее выделить больший массив резервных копий, чем генерировать новые объекты string для каждого вызова.</p>

<p>поскольку строки неизменяемы, каждый вызов оператора + создает новый объект string и копирует данные string в новую строку. поскольку копирование строки занимает время, линейное по длине строки, последовательность из n обращений к оператору + приводит к o (n<sup>2</sup>) время выполнения (квадратичное).</p><p>и наоборот, поскольку stringbuffer является изменяемым, ему не нужно копировать string каждый раз, когда вы выполняете append (), поэтому последовательность из n вызовов append () занимает o(n) время (линейное). это имеет существенное значение только во время выполнения, если вы добавляете большое количество строк вместе.</p>

<p>как уже говорилось, объект string является неизменным, то есть после его создания (см. ниже) его нельзя изменить.</p><blockquote> <p>string x = new string ("что-то"); // или же</p> <p>строка x = "что-то";</p> </blockquote><p>поэтому, когда вы пытаетесь объединить объекты string, значение этих объектов берется и помещается в новый объект string.</p><p>если вместо этого вы используете stringbuffer, переменная которого is, вы постоянно добавляете значения во внутренний список символов (примитивов), который может быть расширен или усечен для соответствия требуемому значению. новые объекты не создаются, только новые символы создаются/удаляются, когда это необходимо для хранения значений.</p>

<p>когда вы объединяете две строки, вы фактически создаете третий объект string в java. использование stringbuffer (или stringbuilder в java 5/6) быстрее, поскольку он использует внутренний массив символов для хранения строки, а когда вы используете один из его методов add (...), он не создать новый объект string. вместо этого stringbuffer/buider добавляет внутренний массив.</p><p>в простых конкатенациях на самом деле не проблема, соединяете ли вы строки с помощью stringbuffer/builder или оператора '+', но при выполнении большого количества конкатенаций строк вы увидите, что использование stringbuffer/builder намного быстрее.</p>

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

<p>я думаю, что самый простой ответ: это быстрее.</p><p>если вы действительно хотите знать все о себе, вы всегда можете взглянуть на источник самостоятельно:</p><p> <a href="http://www.sun.com/software/opensource/java/getinvolved.jsp">http://www.sun.com/software/opensource/java/getinvolved.jsp</a> </p><p> <a href="http://download.java.net/jdk6/latest/archive/">http://download.java.net/jdk6/latest/archive/</a> </p>

<p>в разделе <a href="http://java.sun.com/docs/books/jls/third\_edition/html/expressions.html#15.18.1">оператор конкатенации строк +</a> спецификации языка java содержится дополнительная справочная информация о том, почему оператор + может быть настолько медленным.</p>