Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

Ответ 2

Для простых конкатенаций типа:

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

Ответ 3

Нельзя быть быстрее, чем другой. Это было неверно до 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() их всех.

Ответ 4

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

So

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

становится

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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

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

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

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

Ответ 7

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

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

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

Ответ 8

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

Остерегайтесь, что использование + = фактически назначает новую копию строки.

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

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

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

Скомпилирован для:

String foo = "abc"; 

Ответ 9

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

myString = myString + "XYZ"

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

Ответ 10

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

Ответ 11

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

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

Ответ 12

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

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

Ответ 13

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

Строка x = новая строка ( "что-то" );//или

Строка x = "something";

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

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

Ответ 14

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

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

Ответ 15

Дополнительная информация:

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

Ответ 16

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

Ответ 18

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