Почему использовать StringBuffer в Java вместо оператора объединения строк

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

54
задан Rob Hruska 11 April 2010 в 22:13
поделиться

18 ответов

Лучше использовать 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 с, о которой Вы не заботитесь.

62
ответ дан Calum 7 November 2019 в 07:40
поделиться

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

0
ответ дан Benedikt Waldvogel 7 November 2019 в 07:40
поделиться

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

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

http://www.sun.com/software/opensource/java/getinvolved.jsp

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

0
ответ дан rgcb 7 November 2019 в 07:40
поделиться

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

0
ответ дан Ivan Bosnic 7 November 2019 в 07:40
поделиться

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

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

1
ответ дан Alexandre Brasil 7 November 2019 в 07:40
поделиться

Как сказано, Строковый объект ummutable, означая, как только он создается (см. ниже), он не может быть изменен.

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

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

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

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

1
ответ дан Christian P. 7 November 2019 в 07:40
поделиться

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

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

1
ответ дан Adam Rosenfield 7 November 2019 в 07:40
поделиться

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

1
ответ дан Eclipse 7 November 2019 в 07:40
поделиться

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

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

1
ответ дан 7 November 2019 в 07:40
поделиться

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

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

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

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

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

Имеет, компилируется в:

String foo = "abc"; 
3
ответ дан jb. 7 November 2019 в 07:40
поделиться

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

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

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

3
ответ дан Alan Krueger 7 November 2019 в 07:40
поделиться

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

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 избегают этого, потому что они изменяемы, и так не должны продолжать копировать те же данные много раз (пока существует пространство для копирования в в их внутреннем буфере). Они стараются не выполнять выделение и копируют пропорциональный количеству, добавляет сделанный over-allocing, их буфер пропорцией его текущего размера, давая амортизировал O (1) добавление.

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

5
ответ дан Brian 7 November 2019 в 07:40
поделиться

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

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

7
ответ дан slipset 7 November 2019 в 07:40
поделиться

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

Так

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

становится

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

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

9
ответ дан jodonnell 7 November 2019 в 07:40
поделиться

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

20
ответ дан André Chalella 7 November 2019 в 07:40
поделиться

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

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

довольно бессмысленно использовать StringBuffer - в качестве [1 110], 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 7 November 2019 в 07:40
поделиться

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

myString = myString + "XYZ"

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

1
ответ дан Loren Segal 7 November 2019 в 07:40
поделиться

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

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
ответ дан 7 November 2019 в 07:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: