Зачем использовать StringBuffer в Java вместо оператора конкатенации строк - PullRequest
53 голосов
/ 15 сентября 2008

Кто-то сказал мне, что более эффективно использовать StringBuffer для объединения строк в Java, чем использовать оператор + для String s. Что происходит под капотом, когда вы делаете это? Что StringBuffer делает по-другому?

Ответы [ 18 ]

61 голосов
/ 15 сентября 2008

Лучше использовать StringBuilder (это несинхронизированная версия; когда вы строите строки параллельно?) В наши дни почти во всех случаях, но вот что происходит:

Когда вы используете + с двумя строками, он компилирует код следующим образом:

String third = first + second;

Примерно так:

StringBuilder builder = new StringBuilder( first );
builder.append( second );
third = builder.toString();

Поэтому для маленьких примеров это обычно не имеет значения. Но когда вы строите сложную строку, вам часто приходится иметь дело с гораздо большим, чем это; например, вы можете использовать много разных добавляемых операторов или цикл вроде этого:

for( String str : strings ) {
  out += str;
}

В этом случае новый экземпляр StringBuilder и новый String (новое значение out - String s являются неизменяемыми) требуются в каждой итерации. Это очень расточительно. Замена этого на один StringBuilder означает, что вы можете просто создать один String и не заполнять кучу String s, которые вам не нужны.

42 голосов
/ 15 сентября 2008

Для простых объединений, таких как:

String s = "a" + "b" + "c";

Использовать StringBuffer довольно бессмысленно - как указывало Джодоннелл , оно будет умно переведено на:

String s = new StringBuffer().append("a").append("b").append("c").toString();

НО очень бесполезно объединять строки в цикле, например:

String s = "";
for (int i = 0; i < 10; i++) {
    s = s + Integer.toString(i);
}

Использование строки в этом цикле сгенерирует в памяти 10 промежуточных строковых объектов: «0», «01», «012» и т. Д. При написании того же с использованием StringBuffer вы просто обновляете некоторый внутренний буфер StringBuffer и не создаете те промежуточные строковые объекты, которые вам не нужны:

StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
    sb.append(i);
}

На самом деле для приведенного выше примера вы должны использовать StringBuilder (введено в Java 1.5) вместо StringBuffer - StringBuffer немного тяжелее, поскольку все его методы синхронизированы.

20 голосов
/ 15 сентября 2008

Один не должен быть быстрее другого. Это было не так до Java 1.4.2, потому что при объединении более двух строк с использованием оператора "+" промежуточные объекты String будут создаваться в процессе построения окончательной строки.

Однако, как гласит JavaDoc для StringBuffer , по крайней мере, поскольку Java 1.4.2 с использованием оператора "+" компилируется до создания StringBuffer и append() с множеством строк. Так что нет разницы, по-видимому.

Однако, будьте осторожны при добавлении строки в другую внутри цикла! Например:

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;
}

Имейте в виду, однако, что объединение нескольких строк с помощью «+» чище, чем append(), включая их все.

9 голосов
/ 15 сентября 2008

Под капотом он фактически создает и добавляет к StringBuffer, вызывая toString () для результата. Так что на самом деле не имеет значения, какой вы используете больше.

Итак

String s = "a" + "b" + "c";

становится

String s = new StringBuffer().append("a").append("b").append("c").toString();

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

7 голосов
/ 15 сентября 2008

Я думаю, что с учетом jdk1.5 (или выше) и вашей конкатенации поточно-ориентированной, вы должны использовать StringBuilder вместо StringBuffer http://java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs-stringbuilder.html Что касается увеличения скорости: http://www.about280.com/stringtest.html

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

5 голосов
/ 15 сентября 2008

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

string myString="";
for(int i=0;i<x;i++)
{
    myString += "x";
}

будет действовать следующим образом (каждый шаг является следующей итерацией цикла):

  1. построить строковый объект длиной 1 и значением "x"
  2. Создайте новый строковый объект размером 2, скопируйте в него старую строку «x», добавьте «x» в положение 2.
  3. Создайте новый строковый объект размером 3, скопируйте в него старую строку «xx», добавьте «x» в позицию 3.
  4. ... и т. Д.

Как видите, на каждой итерации приходится копировать еще один символ, в результате чего мы выполняем 1 + 2 + 3 + 4 + 5 + ... + N операций в каждом цикле. Это операция O (n ^ 2). Однако, если бы мы заранее знали, что нам нужно только N символов, мы могли бы сделать это за одно выделение, скопировав всего N символов из строк, которые мы использовали, - просто операция O (n).

StringBuffer / StringBuilder избегают этого, потому что они изменчивы, и поэтому не нужно повторять копирование одних и тех же данных снова и снова (при условии, что в их внутреннем буфере есть место для копирования). Они избегают выполнения выделения и копирования пропорционально количеству добавлений, выполненных путем чрезмерного выделения их буфера пропорционально его текущему размеру, давая добавление амортизированного O (1).

Однако стоит отметить, что часто компилятор может автоматически оптимизировать код в стиле StringBuilder (или лучше - поскольку он может выполнять постоянное сворачивание и т. Д.).

3 голосов
/ 15 сентября 2008

AFAIK это зависит от версии JVM, в версиях до 1.5 с использованием "+" или "+ =" фактически каждый раз копируется вся строка.

Помните, что использование + = фактически выделяет новую копию строки.

Как было указано, использование + in циклов включает копирование.

Когда строки, которые соединяются, являются константами времени компиляции, они объединяются во время компиляции, поэтому

String foo = "a" + "b" + "c";

Имеет компиляцию в:

String foo = "abc"; 
3 голосов
/ 15 сентября 2008

Java превращает string1 + string2 в конструкцию StringBuffer, append () и toString (). Это имеет смысл.

Однако в Java 1.4 и более ранних версиях это будет сделано для каждого + оператора в операторе отдельно . Это означало, что выполнение a + b + c приведет к двум конструкциям StringBuffer с двумя вызовами toString (). Если бы у вас была длинная цепочка конкататов, это превратилось бы в настоящий беспорядок. Делать это самостоятельно означало, что вы можете контролировать это и делать это правильно.

Java 5.0 и выше, кажется, делают это более разумно, так что это меньше проблем и, конечно, менее многословно.

1 голос
/ 15 сентября 2008

Как уже говорилось, объект String является неизменным, то есть после его создания (см. Ниже) его нельзя изменить.

String x = new String ("что-то"); // или

String x = "что-то";

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

Если вместо этого вы используете StringBuffer, который является изменяемым, вы постоянно добавляете значения во внутренний список символов (примитивов), который можно расширять или урезать в соответствии с необходимым значением. Новые объекты не создаются, только новые символы создаются / удаляются при необходимости для хранения значений.

1 голос
/ 15 сентября 2008

Класс StringBuffer поддерживает массив символов для хранения содержимого строк, которые вы объединяете, тогда как метод + создает новую строку каждый раз при вызове и добавляет два параметра (param1 + param2).

StringBuffer работает быстрее, потому что 1. он может использовать уже существующий массив для объединения / хранения всех строк. 2. даже если они не помещаются в массив, быстрее выделить больший массив резервных копий, чем генерировать новые объекты String для каждого вызова.

...