Рубрики
Без рубрики

StringBuilder vs StringBuffer в Java

Обзор Java StringBuilder и StringBuffer, указывающий на сходства и различия.

Автор оригинала: baeldung.

1. Обзор

В этой короткой статье мы рассмотрим сходства и различия между StringBuilder и StringBuffer в Java.

Проще говоря, StringBuilder был представлен в Java 1.5 в качестве замены StringBuffer .

2. Сходство

И StringBuilder , и StringBuffer создают объекты, содержащие изменяемую последовательность символов. Давайте посмотрим, как это работает и как это сравнивается с неизменяемым классом String :

String immutable = "abc";
immutable = immutable + "def";

Даже если может показаться , что мы изменяем один и тот же объект, добавляя “def” , мы создаем новый, потому что String экземпляры не могут быть изменены.

При использовании StringBuffer или StringBuilder мы можем использовать метод append() :

StringBuffer sb = new StringBuffer("abc");
sb.append("def");

В этом случае новый объект не был создан. Мы вызвали метод append() в экземпляре sb и изменили его содержимое. StringBuffer и StringBuilder являются изменяемыми объектами.

3. Различия

StringBuffer синхронизирован и, следовательно, потокобезопасен. |/StringBuilder совместим с StringBuffer API, но без гарантии синхронизации.

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

3.1. Производительность

В небольших итерациях разница в производительности незначительна. Давайте проведем быстрый микро-бенчмарк с помощью JMH:

@State(Scope.Benchmark)
public static class MyState {
    int iterations = 1000;
    String initial = "abc";
    String suffix = "def";
}

@Benchmark
public StringBuffer benchmarkStringBuffer(MyState state) {
    StringBuffer stringBuffer = new StringBuffer(state.initial);
    for (int i = 0; i < state.iterations; i++) {
        stringBuffer.append(state.suffix);
    }
    return stringBuffer;
}

@Benchmark
public StringBuilder benchmarkStringBuilder(MyState state) {
    StringBuilder stringBuilder = new StringBuilder(state.initial);
    for (int i = 0; i < state.iterations; i++) {
        stringBuilder.append(state.suffix);
    }
    return stringBuilder;
}

Мы использовали режим по умолчанию Пропускная способность – т. е. Операции в единицу времени (чем выше оценка, тем лучше), что дает:

Benchmark                                          Mode  Cnt      Score      Error  Units
StringBufferStringBuilder.benchmarkStringBuffer   thrpt  200  86169.834 ±  972.477  ops/s
StringBufferStringBuilder.benchmarkStringBuilder  thrpt  200  91076.952 ± 2818.028  ops/s

Если мы увеличим количество итераций с 1k до 1m, то получим:

Benchmark                                          Mode  Cnt   Score   Error  Units
StringBufferStringBuilder.benchmarkStringBuffer   thrpt  200  77.178 ± 0.898  ops/s
StringBufferStringBuilder.benchmarkStringBuilder  thrpt  200  85.769 ± 1.966  ops/s

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

4. Выводы

Проще говоря, Струнный буфер является потокобезопасной реализацией и, следовательно, медленнее, чем StringBuilder .

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

Наконец, как всегда, код, используемый во время обсуждения, можно найти на GitHub .