JavaのStringオブジェクトは、オブジェクト生成時に決定するため、以下のサンプルプログラムのように、「+=」演算子を用いた文字列結合による内容変更が可能ですが、Stringオブジェクトとしては内容変更ができません(演算結果を表す新たなStringオブジェクトが生成されます)。 次々と新たなStringオブジェクトを生成すると、メモリが多く消費され、非効率的です。
それに対して、StringBufferオブジェクトは、オブジェクト生成後であっても内容変更することが可能です。 したがって、内容変更が発生するようなプログラム処理を行う場合は、StringBufferを利用する方が効率的です。
以下にStringオブジェクトの操作の落とし穴について、サンプルプログラムをもとに説明します。
[サンプルプログラム1-1(Stringを使用)]
[実行結果]
以下にローカル変数とStringオブジェクトの状態を説明します。
ループ処理前(宣言の後)の状態
「String str = "富士通,"」という宣言を記述すると、コンパイル時に「"富士通,"」を表す文字列定数オブジェクトが作られ、ローカル変数strはそれを指します。
なお、ローカル変数resultには「null」を代入していますので、何もオブジェクトを指していない状態です。

1回目のループ処理後の状態
「result += str;」を実行すると、新しいStringオブジェクト「"null富士通,"」が作られ、ローカル変数resultはそれを指します。

2回目のループ処理後の状態
2回目に「result += str;」を実行すると、元のStringオブジェクトが変更されるのではなく、新しいStringオブジェクト「"null富士通,富士通,"」が作られ、ローカル変数resultはそれを指します。
その結果、今まで指されていたStringオブジェクトは、どこからも参照されなくなり、ガベージ(ごみ)となります。
これらガベージ(ごみ)が頻繁に発生すると、どんどんメモリが消費されていきます。

なお、参照が外れ、どの変数からも参照されなくなったStringオブジェクトは、ガベージ(ごみ)として、ガベージコレクション[注]の対象となります。
注:ガベージコレクション
Java言語には、割り当てたメモリを利用可能かどうか判断し、自動的にメモリを開放するガベージコレクションという機能が導入されています。
ガベージとは「ごみ」、コレクションとは「集める」ことを意味します。
メモリ中に不要になったオブジェクトが占有しているメモリ領域を開放する役割を果たします。
3回目のループ処理後の状態
3回目に「result += str;」を実行すると、3. と同様に、元のStringオブジェクトが変更されるのではなく、新しいStringオブジェクト「"null富士通,富士通,富士通"」が作られ、ローカル変数resultはそれを指します。
その結果、今まで指されていたStringオブジェクトは、どこからも参照されなくなり、ガベージ(ごみ)となります。

4回目のループ処理後の状態
4回目に「result += str;」を実行すると、3.、4. と同様に、元のStringオブジェクトが変更されるのではなく、新しいStringオブジェクト「"null富士通,富士通,富士通,富士通"」が作られ、ローカル変数resultはそれを指します。
その結果、今まで指されていたStringオブジェクトは、どこからも参照されなくなり、ガベージ(ごみ)となります。

以上のように、Stringオブジェクトは、初回生成後は内容変更が行えない仕様のため、代入による内容入れ替えなどの内容変更処理発生時は、代入のたびにオブジェクトが生成されます。
特にループ中でStringオブジェクトを呼び出した場合は、途中の操作結果のためにStringオブジェクトがループ回数と同じ個数だけ生成され、メモリの浪費になりますので、注意が必要です。
この問題を解決するため(余分なメモリを消費しないため)に、StringBufferを利用します。 改善したサンプルプログラムを以下に示します。
[サンプルプログラム1-2(StringBufferを使用)]
[実行結果]
文字列操作を頻繁に行なう場合は、java.lang.StringBufferクラスで操作してからStringに変換すると生成されるオブジェクトの数がはるかに少なくて済みます。
2つのサンプルプログラムは同じ処理を行っていますが、Stringを使用した場合はループ処理中にStringオブジェクトをループ回数分生成します。 それに対して、StringBufferを使用した場合はStringBufferオブジェクトと最後にStringオブジェクトを1つ生成するだけで済みます。
ただし、StringBufferを利用した場合であっても、初期サイズには注意が必要です。
StringBufferオブジェクトのデフォルトの初期サイズ(初期サイズを明示的に指定しない場合)は、16文字分です(初期文字列を指定した場合は「初期文字列+16文字」となります)。
なお、Java言語は半角・全角に関係なく、一文字16bitのUNICODEを使用しているため、16文字に半角・全角の違いはありません。
従って16文字を超える場合が事前にわかっている場合は、必ず初期サイズを明示的に指定してください。
もし、初期サイズを超えた場合は、Stringオブジェクトと同様にその時点で新たにオブジェクト生成が必要になります。
具体的に、上記の「サンプルプログラム1-2」のように初期サイズを指定します(当サンプルでは200文字を初期サイズとしています)。
最後に、富士通のJava開発規約チェッカ「SIMPLIA/JF Kiyacker」をご紹介します。
当ツールを使用すると、最初にご紹介した「サンプルプログラム1-1」を読み込むことで、非効率なStringを使用「頻繁に文字列を結合する時はStringBufferを使う」のチェック(検出)を行うことが可能です。
検出された理由および、どう修正すればよいかを把握することができ、またプログラム中の検出箇所(行数)がダイレクトにわかるため、効率的にプログラム修正を行っていけます。
表示されたソースコード(22行目で問題が検出されたことがわかります)
詳細理由「頻繁に文字列を結合する時はStringBufferを使う」が表示されます
プログラムを修正後にチェックを通すと、今度は検出されません。
(2番目にご紹介した「サンプルプログラム1-2」に修正後、チェックを通すと今度は検出されなくなります)
SIMPLIA/JF Kiyacker
Java言語を使用する開発プロジェクトの開発規約の作成をサポートし、作成された規約に沿って開発されているかをチェックするためのソフトウェアです。
プログラムを動作させる動的テストを実施する前に、SIMPLIA/JF Kiyackerにて静的テストを行うことにより、プログラムの品質向上を支援します。
大きく以下の2つの機能をもちます。
<規約チェック機能>
開発者が準拠しなければいけない規約集の提供と、そのチェックを支援します。
ユーザ規約により、プロジェクト独自の規約を適時追加できます(カスタマイズが可能)。
命名規約やコメント規約などをチェックできます。
<コードレビュー支援機能>
規約ではない、プログラムソース上に記述しない方が良い命令や、初心者が誤りやすい記述についてのソースコードレビューを支援します。
今回のチェックで検出された「頻繁に文字列を結合する時はStringBufferを使う」は、当機能でチェックできます。
StringオブジェクトとStringBufferのチェック(検出)が可能な主なものには、以下があります。
なお、SIMPLIA/JF Kiyackerの詳細につきましては、以下の関連情報をご参照ください。
関連情報
また、本文中でご紹介した2つのサンプルプログラムもこちらからダウンロードできますので、是非ご活用ください。