StringBuilder vs StringBuffer en Java

1. Información general

En este breve artículo, veremos las similitudes y diferencias entre StringBuilder y StringBuffer en Java.

En pocas palabras, StringBuilder se introdujo en Java 1.5 como reemplazo de StringBuffer .

2. Similitudes

Tanto StringBuilder como StringBuffer crean objetos que contienen una secuencia mutable de caracteres. Veamos cómo funciona esto y cómo se compara con una clase String inmutable :

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

Aunque parezca que estamos modificando el mismo objeto agregando "def" , estamos creando uno nuevo porque las instancias de String no se pueden modificar.

Cuando usamos StringBuffer o StringBuilder, podemos usar el método append () :

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

En este caso, no se creó ningún objeto nuevo. Hemos llamado al método append () en la instancia de sb y hemos modificado su contenido. StringBuffer y StringBuilder son objetos mutables.

3. Diferencias

StringBuffer está sincronizado y, por lo tanto, es seguro para subprocesos. StringBuilder es compatible con laAPI StringBuffer pero sin garantía de sincronización.

Debido a que no es una implementación segura para subprocesos, es más rápida y se recomienda usarla en lugares donde no hay necesidad de seguridad para subprocesos.

3.1. Actuación

En pequeñas iteraciones, la diferencia de rendimiento es insignificante. Hagamos un micro-benchmark rápido con 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; }

Hemos utilizado el modo de rendimiento predeterminado , es decir, operaciones por unidad de tiempo (una puntuación más alta es mejor), lo que da:

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

Si aumentamos el número de iteraciones de 1k a 1m, obtenemos:

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

Sin embargo, tengamos en cuenta que se trata de un micro-benchmark, que puede tener o no un impacto real en el rendimiento real de una aplicación en el mundo real.

4. Conclusiones

En pocas palabras, StringBuffer es una implementación segura para subprocesos y, por lo tanto, más lenta que StringBuilder .

En programas de un solo subproceso, podemos tomar de StringBuilder . Sin embargo, la ganancia de rendimiento de StringBuilder sobre StringBuffer puede ser demasiado pequeña para justificar su reemplazo en todas partes. Siempre es una buena idea perfilar la aplicación y comprender sus características de rendimiento en tiempo de ejecución antes de realizar cualquier tipo de trabajo para reemplazar una implementación por otra.

Finalmente, como siempre, el código utilizado durante la discusión se puede encontrar en GitHub.