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 .