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 .