StringBuilder и StringBuffer в Java

StringBuilder и StringBuffer в Java

1. обзор

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

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

2. сходства

ИStringBuilder, иStringBuffer создают объекты, содержащиеa mutable sequence of characters. Давайте посмотрим, как это работает и в сравнении с неизменным классом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 совместим с APIStringBuffer, но без гарантии синхронизации.

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

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

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

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

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

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. Выводы

Проще говоря,StringBuffer является поточно-ориентированной реализацией и поэтому медленнее, чемStringBuilder.

В однопоточных программах мы можем взятьStringBuilder. Тем не менее,the performance gain of StringBuilder over StringBuffer may be too small to justify replacing it everywhere. Всегда полезно профилировать приложение и понимать его характеристики производительности во время выполнения, прежде чем выполнять какую-либо работу по замене одной реализации другой.

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