it-swarm-ru.tech

Зачем использовать StringBuffer в Java вместо оператора конкатенации строк

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

53
harry

Лучше использовать 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 - Strings являются неизменяемыми) требуются в каждой итерации. Это очень расточительно. Замена этого на одно StringBuilder означает, что вы можете просто создать одно String и не заполнять кучу Strings, которые вас не интересуют.

61
Calum

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

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

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

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 немного тяжелее, так как все его методы синхронизированы.

42
tkokoszka

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

Однако, как говорится в JavaDoc for 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()ing их всех.

20
André Chalella

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

Так

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

становится

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

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

9
jodonnell

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

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

7
slipset

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

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 (или лучше - поскольку он может выполнять постоянное свертывание и т.д.).

5
Brian

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

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

Как было указано, использование + в циклах подразумевает копирование.

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

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

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

String foo = "abc"; 
3
jb.

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

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

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

3
Alan Krueger

Дальнейшая информация:

StringBuffer - это потокобезопасный класс


public final class StringBuffer extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
     public synchronized StringBuffer append(StringBuffer stringbuffer)
    {
        super.append(stringbuffer);
        return this;
    }
// .. skip ..
}

Но StringBuilder не является поточно-ориентированным, поэтому по возможности быстрее использовать StringBuilder


public final class StringBuilder extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
    public StringBuilder append(String s)
    {
        super.append(s);
        return this;
    }
// .. skip ..
}

1
Eric Yung

StringBuffer является изменяемым. Он добавляет значение строки к объекту same без создания экземпляра другого объекта. Делать что-то вроде:

myString = myString + "XYZ"

создаст новый объект String.

1
Loren Segal

Чтобы объединить две строки с помощью "+", необходимо выделить новую строку с пробелом для обеих строк, а затем скопировать данные из обеих строк. StringBuffer оптимизирован для объединения и выделяет больше места, чем необходимо изначально. Когда вы объединяете новую строку, в большинстве случаев символы могут быть просто скопированы в конец существующего строкового буфера.
Для объединения двух строк оператор '+', вероятно, будет иметь меньше накладных расходов, но по мере того, как вы объединяете больше строк, StringBuffer выходит вперед, используя меньшее количество выделяемой памяти и меньшее количество копируемых данных.

1
Eclipse

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

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

1
Matt Novinger

Поскольку строки неизменяемы, каждый вызов оператора + создает новый объект String и копирует данные String в новую строку. Поскольку копирование строки занимает время, линейное по длине строки, последовательность из N обращений к оператору + приводит к O (N2) время выполнения (квадратичное).

И наоборот, поскольку StringBuffer является изменяемым, ему не нужно копировать String каждый раз, когда вы выполняете Append (), поэтому последовательность из N вызовов Append () занимает O(N) время (линейное). Это имеет существенное значение только во время выполнения, если вы добавляете большое количество строк вместе.

1
Adam Rosenfield

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

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

Строка x = "что-то";

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

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

1
Christian P.

Когда вы объединяете две строки, вы фактически создаете третий объект String в Java. Использование StringBuffer (или StringBuilder в Java 5/6) быстрее, поскольку он использует внутренний массив символов для хранения строки, а когда вы используете один из его методов add (...), он не создать новый объект String. Вместо этого StringBuffer/Buider добавляет внутренний массив.

В простых конкатенациях на самом деле не проблема, соединяете ли вы строки с помощью StringBuffer/Builder или оператора '+', но при выполнении большого количества конкатенаций строк вы увидите, что использование StringBuffer/Builder намного быстрее.

1
Alexandre Brasil

Поскольку строки являются неизменяемыми в Java, каждый раз, когда вы объединяете строку, в памяти создается новый объект. StringBuffer использует тот же объект в памяти.

0
Ivan Bosnic

Я думаю, что самый простой ответ: это быстрее.

Если вы действительно хотите знать все о себе, вы всегда можете взглянуть на источник самостоятельно:

http://www.Sun.com/software/opensource/Java/getinvolved.jsp

http://download.Java.net/jdk6/latest/archive/

0
rgcb

В разделе Оператор конкатенации строк + Спецификации языка Java содержится дополнительная справочная информация о том, почему оператор + может быть настолько медленным.

0
Benedikt Waldvogel