タグ別アーカイブ: スレッド

StringBufferとStringBuilderの違い

javaには文字列を可変的に扱うためのクラスとして StringBuffer 及び StringBuilder という2つのクラスが用意されています。
この2つのクラスは文字をバッファとして取り扱い、任意のタイミングで任意の文字列を詰め込むことができ、非常に使用頻度の高いクラスとなっています。
されとてこの2つのクラス同じような事できるのですが具体的に、どう異なるかご存知でしょうか?
本記事ではこの2つのクラスの差について調査します。

なお対象としては java8 の API を対象とします。

調査する

公式の java8 API を参照してみましょう。参考リンクについては本記事の末尾に記載しますので参照してください。

と、いきなり回答にたどり着いてしまうのですが StringBuilder のマニュアルを確認すると、以下のような記載があります。

文字の可変シーケンスです。このクラスは、StringBufferと互換性があるAPIを提供しますが、同期化は保証されません。このクラスは、文字列バッファが単一のスレッド(一般的なケース)により使用されていた場合のStringBufferの簡単な代替として使用されるよう設計されています。このクラスは、ほとんどの実装で高速に実行されるので、可能な場合は、StringBufferよりも優先して使用することをお薦めします。

つまり StringBuilder はスレッドセーフではないシンプルなケースに適用でき、それ以外の場合は StringBuffer を使用すると良い、ということです。

シンプルに使い所をまとめると

StringBuffer

スレッドセーフであるので複数スレッドから参照されるような場合に適切
スレッド間の排他制御を実装している。

StringBuilder

シングルスレッドで排他処理の必要がないシンプルな処理の場合に適切
使用できる場合はこちらのクラスを使用したほうがStringBuilderよりも高速に処理が行える

というところですね。

実験してみる

実装レベルでの検証のために下記のようなサンプルコードを用意しました。
ちなみに groovy コードとなりますのでご留意ください。

同じ文字列バッファを共有したスレッドを10個作成してそれぞれのスレッドから100回書き込みを行います。
最終的にバッファの内容がどうなっているかを確認するシンプルなコードです。
これを文字列バッファをStringBufferとStringBuilderそれぞれに切り替えて結果を確認してみます。

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

class WriteThread implements Runnable {
  int num
  StringBuffer sb

  public WriteThread(int num, StringBuffer sb) {
    this.num = num
    this.sb = sb
  }

  @Override
  public void run() {
    for (int i=0; i<100; i++) {
      this.sb.append("I am thread ${num}.\n")
    }
  }
}

StringBuffer sharedBuilder = new StringBuffer()
ExecutorService executor = Executors.newFixedThreadPool(10)
for (int i=0; i<10; i++) {
  executor.submit(new WriteThread(i, sharedBuilder))
}
executor.shutdown()
executor.awaitTermination(10, TimeUnit.SECONDS)

println sharedBuilder.toString()

StringBuffer

結果の一部のみを抜粋しますが、きれいに文字列が出力できていることが確認できます。

...
I am thread 6.
I am thread 1.
I am thread 3.
I am thread 7.
I am thread 3.
I am thread 1.
I am thread 6.
...

StringBuilder

対して StringBuilder の方は下記のように破損している箇所が見られます。
(※見れられないこともあります)
書き込みが衝突してしまったということです。

...
I am thread 7.
I am thread 9.
ead 8.
thread 9.
ad 1.
ad 0.
I am thread 5.
...

まとめ

とりあえずマルチスレッドなケースが求められる場面では StringBuffer を使いましょう。
にしても名前がややこしいな・・・どっちがどっちだかわからなくなる。

参考

StringBuilder
StringBuffer