StringBuilder vs StringBuffer in Java

1. Panoramica

In questo breve articolo, esamineremo le somiglianze e le differenze tra StringBuilder e StringBuffer in Java.

In poche parole, StringBuilder è stato introdotto in Java 1.5 in sostituzione di StringBuffer .

2. Somiglianze

Sia StringBuilder che StringBuffer creano oggetti che contengono una sequenza mutevole di caratteri. Vediamo come funziona e come si confronta con una classe String immutabile :

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

Anche se può sembrare che stiamo modificando lo stesso oggetto aggiungendo "def" , ne stiamo creando uno nuovo perché le istanze di String non possono essere modificate.

Quando si utilizza StringBuffer o StringBuilder, è possibile utilizzare il metodo append () :

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

In questo caso, non è stato creato alcun nuovo oggetto. Abbiamo chiamato il metodo append () sull'istanza di sb e ne abbiamo modificato il contenuto. StringBuffer e StringBuilder sono oggetti modificabili.

3. Differenze

StringBuffer è sincronizzato e quindi thread-safe. StringBuilder è compatibile con l'API StringBuffer ma senza alcuna garanzia di sincronizzazione.

Poiché non è un'implementazione thread-safe, è più veloce e si consiglia di usarlo in luoghi in cui non è necessaria la thread safety.

3.1. Prestazione

In piccole iterazioni, la differenza di prestazioni è insignificante. Facciamo un rapido micro-benchmark 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; }

Abbiamo utilizzato la modalità Throughput predefinita , ovvero operazioni per unità di tempo (un punteggio più alto è migliore), che fornisce:

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

Se aumentiamo il numero di iterazioni da 1k a 1m, otteniamo:

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

Tuttavia, teniamo presente che questo è un micro-benchmark, che può o meno avere un impatto reale sulle prestazioni effettive e reali di un'applicazione.

4. Conclusioni

In poche parole, StringBuffer è un'implementazione thread-safe e quindi più lenta di StringBuilder .

Nei programmi a thread singolo, possiamo prendere StringBuilder . Tuttavia, il miglioramento delle prestazioni di StringBuilder rispetto a StringBuffer potrebbe essere troppo piccolo per giustificare la sua sostituzione ovunque. È sempre una buona idea profilare l'applicazione e comprenderne le caratteristiche delle prestazioni di runtime prima di eseguire qualsiasi tipo di lavoro per sostituire un'implementazione con un'altra.

Infine, come sempre, il codice utilizzato durante la discussione può essere trovato su GitHub.