こんにちは。ユーキです。
今日は、StringとStringBuffferの使い分けの目安について、お話したいと思います。
Stringクラスとは
文字列を扱うクラスです。Javaで文字列を扱う場合には、避けては通れない基礎中の基礎となるクラスです。多くのメソッドの引数にも指定されています。
Javaを学習する際でも、プリミティブ型(基本型)と並び立つクラスです。
StringBufferクラスとは
Stringクラスの拡張と考えれるのが、一番理解しやすいと思います。
Java API リファレンスによると、「スレッドセーフな可変の文字列」と表現されています。
どういうことなのかは、次で説明をしていきたいと思います。
StringクラスとStringBufferクラスの違い
StringBufferは、「スレッドセーフな可変の文字列」でした。では、Stringクラスはどうかというと、Stringクラスは固定の文字列となります。
ん?と思ったあなた。わかってますね。そうです。Stringクラスも文字の連結処理は行うにも関わらず、固定の文字列とはどういうことなのか。
実際にコードを書いて説明します。
String txt1 = new String("Good");
txt1 = txt1 + "Morning";
txt1 = txt1 + ":おはよう!";
String txt2 = "Good" + "Morning" + ":おはよう!";
StringBuffer sb = new StringBuffer();
sb.append("Good");
sb.append("Morning");
sb.append(":おはよう!");
いずれも、「GoodMorning:おはよう!」という文字列を生成しています。
コード上では、どの書き方をしても得られる結果は同じです。違うのは、メモリの使われ方です。
実はStringオブジェクトは、文字連結をする際に新しいオブジェクトを内部で生成しています。
どういうことかというと、「txt1 = txt1 + “Morning”;」を実行すると、内部では以下のように処理をされます。
txt1 = new String(txt1 + "Morning");
新しいオブジェクトが生成されています。つまり、その生成されたオブジェクト分だけメモリが使用されることとなります。
これを繰り返すと、無駄なメモリを消費してしまうため、文字連結をする際にはStringBufferが推奨されます。StringBufferであれば、新たなオブジェクトが生成されることなく、文字を連結していくことが可能です。
なお、「String txt2 = “Good” + “Morning” + “:おはよう!”;」このコードについては、生成されるStringオブジェクトは1つだけになるため、さほど問題でありません。
検証
では、検証コードを書いて検証してみましょう。
public static void main(String[] args) {
writeMemory("実施前:");
String str = "";
String txt1 = new String();
for(int i = 0; i < 10000; i++) {
txt1 = txt1 + "Good";
txt1 = txt1 + "Morning";
txt1 = txt1 + ":おはよう!";
}
writeMemory("String txt1");
StringBuffer sb = new StringBuffer();
for(int i = 0; i < 10000; i++) {
sb.append("Good");
sb.append("Morning");
sb.append(":おはよう!");
}
writeMemory("StringBuffer");
}
public static void writeMemory(String type) {
long free = Runtime.getRuntime().freeMemory() / 1024/ 1024;
long total = Runtime.getRuntime().totalMemory() / 1024/ 1024;
long used = total - free;
System.out.println("使用メモリ量:" + used + "MB" + " (" + type + ")");
}
5回実行した結果は以下のようになりました。
回数 | String (MB) | StringBuffer(MB) |
1 | 205 | 2 |
2 | 152 | 1 |
3 | 107 | 2 |
4 | 140 | 1 |
5 | 273 | 1 |
一目瞭然ですね。
繰り返し処理内で、Stringでの文字連結は危険ということがよくわかりますね。
どう使い分ける
上記を踏まえ、Stringの文字連結が危険な書き方であることがわかりました。では、どのように使い分けるか。
個人の好みもありますが、私自身は、連結処理が3行以上になるような場合は、StringBufferを使うようにしています。
また、繰り返し処理で、文字連結する際は、基本的にはStringBufferを選択するのが無難でしょう。
StringBuilderはどうなの?
Java1.5から登場したクラスです。
API リファレンスによると、「ほとんどの実装で高速に実行される」とあります。StringBufferの代替として使用できるようになっているそうですが、「複数のスレッドで使用するには安全ではありません。このような同期が必要な場合は、StringBufferを使用することをお薦めします。」とも記載されています。
私は実務でこのクラスが実装されているのをみたことはありません。個人的には、StringBufferを使用しておくのが無難なように思います。
最後に
実行される処理は同じでも、メモリの使い方が異なるため、使い分けが必要となります。
実務では、100万回繰り返すというような処理を行うこともありますので、こういったメモリの扱われ方も知って、メモリリークを行さないコードを書くように心がけていただけると幸いです。