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. Выводы
Проще говоря, StringBuffer
является потокобезопасной реализацией и, следовательно, медленнее, чем StringBuilder
.
В однопоточных программах мы можем взять StringBuilder
. Тем не менее, прирост производительности StringBuilder
по сравнению со StringBuffer
может быть слишком мал, чтобы оправдать его замену повсюду. Всегда полезно профилировать приложение и понять его характеристики производительности во время выполнения, прежде чем выполнять какую-либо работу по замене одной реализации другой.
Наконец, как всегда, код, использованный во время обсуждения, можно найти на GitHub .