Javaには文字列を操作するときに利用するクラスとして、String、StringBuffer、StringBuilderがあります。それぞれの違いと使い分けについて紹介いたします。
まずは次のサンプルプログラムを実行してみてください。
サンプルコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class CompareTimeSample { static void doString() { String str = new String(""); for (int i = 0; i < 20000; i++) { str = str.concat("A"); } } static void doStringBuffer() { StringBuffer strbf = new StringBuffer(""); for (int i = 0; i < 2000000; i++) { strbf.append("A"); } } static void doStringBuilder() { StringBuilder strbl = new StringBuilder(""); for (int i = 0; i < 2000000; i++) { strbl.append("A"); } } public static void main(String[] args) { long start1 = System.currentTimeMillis(); doString(); long time1 = System.currentTimeMillis() - start1; System.out.println("String 処理時間: " + time1 + " ミリ秒"); long start2 = System.currentTimeMillis(); doStringBuffer(); long time2 = System.currentTimeMillis() - start2; System.out.println("StringBuffer 処理時間: " + time2 + " ミリ秒"); long start3 = System.currentTimeMillis(); doStringBuilder(); long time3 = System.currentTimeMillis() - start3; System.out.println("StringBuilder 処理時間: " + time3 + " ミリ秒"); } } |
String、StringBuffer、StringBuilderの各クラスを利用して、文字列を生成しています。生成している文字列は、はじめは”A”のみで、次は”AA”、そして”AAA”・・・と、”A”という文字を次々につなげて行きます。そして、クラスごとにどれくらい時間がかかったのかを出力しています。処理時間の計算には、現在の時刻をミリ秒単位で計測できる SystemクラスのcurrentTimeMillis()メソッドを利用します。処理後の時刻から処理前の時刻を引くことで、処理にかかった時間を計測し、出力しています。
【実行結果】
String 処理時間: 110 ミリ秒
StringBuffer 処理時間: 22 ミリ秒
StringBuilder 処理時間: 16 ミリ秒
10回行った時の平均は次のようになりました。
String 102.8 ミリ秒
StringBuffer 29.3 ミリ秒
StringBuilder 6.9 ミリ秒
実行環境により、処理時間は大きく異なることがありますが、処理時間の平均の大小関係は変わりません。
このように処理時間が異なる理由は、各クラスで文字列を扱うときの仕組みが異なるからです。特にStringは繰り返しの回数が他の2クラスに比べてかなり少ないのに処理時間は一番かかっています。
Stringは文字列を生成するとき、”A”を生成したあと、”AA”を生成しますが、”A”の後ろにくっつけるようなイメージではなく、”AA”という文字を新たに生成します。そのとき”A”はゴミとなってしまいます。(このゴミはJavaのガベージコレクション機能によって自動的に削除されます。)Stringの文字列は一度生成したら内容を変更できず、文字列の変更をする場合は、内部的に新たな文字列を毎回生成しています。
それに対してStringBufferとStringBuilderは”A”を生成した後、その後ろに追加するようなイメージで”AA”を生成しています。Stringと他の2つのクラスの処理時間が大きく異なるのはこのためです。
StringBufferとStringBuilderの違いは、スレッドを利用した処理に対応できるかできないかです。スレッドについては、今回は割愛しますが、特定の状況下では、StringBuilderは文字列が正しく変更されない可能性があり、StringBufferはそれを防ぐことができます。裏を返せば、それを防ぐための機構が備わっており、その処理をする時間が必要ということです。よって、少しではありますが、特に処置をしないStringBuilderの方が早くなっています。
では、Stringを使うメリットは何かと言うと、メモリの節約です。StringBufferやStringBuilderは文字数が増えても大丈夫なようにあらかじめ多めにメモリを確保しますが、Stringの場合は一度生成したら内容は変更されないので、余分なメモリを用意しておかなくて良いのです。とは言え、先ほど紹介したように、Stringは文字列を編集するたびに新しい文字列を生成し、メモリを無駄にする動きをしますので、文字列の編集を繰り返すような場合はStringの利用を避けた方が良いでしょう。