<<前ページ目次次ページ>>
文字列 その2
〜 java.lang.StringBuffer 〜
  同じ文字列を扱う場合でも、Stringでは都合が悪い、というときは、こちらのStringBufferをどうぞ。
つなげましょ
 ちょっと前の章の続き。クラスStringの文字列連結のメソッドconcatを見てみましょう。文字列s(中身はabcとします)に「de」を連結してみましょう。


  @で新たに「abcde」のインスタンスが生成されます。ここで少し時間が掛かります。次にAでこのインスタンスの参照値がsに代入されます。これによりピンクの古くからあった文字列のインスタンスは、ガベージコレクションの対象となります。メソッドtoUpperCaseでやったのと同じですね。

 もしこの処理が繰り返し行われるとどうなるでしょう。インスタンス中の文字列の変更はできないため、新たなインスタンスの生成と文字列のコピーに時間がかかり、古いインスタンスはガベージコレクションで消されるまで、メモリに居座るのです。どのくらい繰り返されるか、あるいはインスタンスの大きさによっても変わってきますが、なんと効率の悪いことでしょう。それもこれも文字列の変更ができないせいです。

 そこで登場するのがクラスStringBufferです。このクラスのインスタンス中の文字列は変更できます。もし文字列を変更する処理がたびたび行われるのであれば、こちらのクラスを使いましょう。

 こちらも連結でStringとの違いを見てみましょう。メソッドappendを使います。

変数sのインスタンス中の文字列そのものが変更され、「abcde」になります。appendの戻り値はインスタンスの参照値(この場合sの値)になります。ここでは特に使っていません。
 余計なインスタンスの生成は行われないし、ガベージコレクションの対象となるインスタンスも出ません。

 appendには何種類もあります。メソッド名のオーバーロードですね。引数にStringを指定する上の例以外に、intやchar、booleanでもOkです。文字列に直して連結してくれます。次はint型を指定した例です。
   StringBuffer s = new StringBuffer(“abc”);
   s.append(123);
でsは「abc123」になります。


コンストラクタ

 StringBufferのコンストラクタにもいろいろあります。ここで生成されるインスタンス中の文字列の入る場所を文字列バッファと呼びます。
 文字列バッファの容量(何文字入るか)はコンストラクタで初期値を与えることができます。もし文字列の操作でドンドン長くなり入りきらなくなった場合などは、その容量も自動的に増えていきます。ただ長い文字列を扱うとあらかじめわかっている場合は、適当な文字数をコンストラクタで指定しておくとよいでしょう。

StringBuffer s = new StringBuffer();
 容量の指定がないと16文字分の文字列バッファが取られます。文字は何も入っていません。

StringBuffer s = new StringBuffer(1000);
 1000文字分の容量の文字列バッファが取られます。

String t = "abc";
StringBuffer s = new StringBuffer(t);
 Stringの引数が指定されていると、その文字列を初期値とした文字列バッファが取られます。

StringBuffer s = new StringBuffer("abc");
 文字列リテラルはString型なので、このような指定もできます。

やってしまいそうなのがこれ。
StringBuffer s = "abc";
 コンパイルエラーになります。StringからStringBufferへの直接の代入はできません。


他にもいろいろ

 StringBufferのメソッドの使用例をいくつか挙げておきます。この他にもたくさんありますし、同じメソッド名でも引数のちがうものがあります。メソッド名のオーバーロードです。詳しくはAPIをご覧ください。


 appendは最後につなげるのでしたが、@のinsertは途中や先頭に入れることができます。文字列バッファの文字はその位置を先頭から0,1,2,3,…と指定します。(Stringと同じですね。)最初の実引数が2なので、3文字目の前に、2番目の実引数xyzが挿入されます。
 Aは文字列の一部分を削除します。実引数は1と3なので、位置1から、位置3の直前の文字、つまり位置2までの2文字が削除されます。位置1〜位置3ではなく、位置1〜位置2が削除されることに注意しましょう。
 Bは途中の何文字分かを他の文字列に入れ替えます。文字位置の指定はAのdeleteと同様です。
 Cは指定の位置の1文字だけの入れ替えです。
 Dは文字列をさかさまにします。
 EはStringBufferからStringへの変換です。同じ文字列を扱うからといって、
   s2 = s1;
のような直接の代入はできません。他のクラス同様、Stringへの変換toStringを使いましょう。

 他にも文字数を求めるlength(文字列バッファの容量ではない、容量はcapacityで求められます)やStringと同様の働きをするindexOfなどもあります。


う、うごかない

 前章でやったように
   int i = 123;
   System.out.println(i);
ではint型のiの値がInteger.toString(i)あるいはString.valueOf(i)で文字列に変換され、出力されるのでしたね。

 では
   int i = 123;
   System.out.println(“i=” + i);
はどうなるのでしょうか。「+」の左側は文字列リテラルなのでString、右側はint型です。

 演算子「+」は普通数値の加算に使いますが、String型に限って、String同士の連結に使えます。「+」の左右どちらかがStringでない場合、Stringに変換され、連結されます。つまりint型のiの値123は、文字列の123になり、「i=」の後ろに連結されるので、出力は
   i=123
になります。

 さてこの連結方法を利用した次の処理で文字列sはどうなるでしょうか?
[処理1]
                String s = "";
                for (int i = 0; i < n; i++) {
                        s = s + i + '*';
                }
 nが5の場合、文字列sは
   0*1*2*3*4*
となります。これは大丈夫ですか?iが0のとき、sは「0*」になり、iが1のときこれに「1*」が連結されsは「0*1*」になります。とsの文字列はドンドン長くなっていきます。
 
 ところで文字列の「+」での連結は、実際のところ途中StringBufferを使って処理されています。
 上の
   s = s + i + ‘*’


   s = new StringBuffer().append(s).append(i).append(‘*’).toString()

となります。ちょっとわかりにくいので、ばらしてみます。

   StringBuffer sb = new StringBuffer();
   sb.append(s);
   sb.append(i);
   sb.append('*');
   s = sb.toString();

 上から見ていきましょう。まず文字なしのStringBufferのインスタンスを生成します。そこに次々とappendで連結していきます。最後はtoStringでStringに変換し、変数sに代入します。
 sb.append(s)の戻り値はsbの値、つまりこのインスタンスの参照値になるので、続けて
   (sb.appens(s)).append(i)
とできます。左から評価されるので括弧を取り、*をつなげると、
   sb.appned(s).append(i).append(‘*’)
となります。最後にtoStringを呼び出し、sbの部分をコンストラクタの呼び出しにすると、上の長い代入になります。

 では次を見てください。上と同じことをやっているのはいいでしょうか。
[処理2]
                StringBuffer sb1 = new StringBuffer();
                for (int i = 0; i < n; i++) {
                        sb1.append(i).append('*');
                }
 nの値が大きくなると、処理時間に大幅な違いが出てきます。我が家のパソコンでは処理1は処理2の何千倍もの時間が掛かりました。その原因は、処理1の繰り返しの中で毎回ひそかに行われる
   new StringBuffer()
の部分です。インスタンスの生成は時間が掛かります。それを繰り返すのですから大変です。一方処理2の方は最初に一度作ったインスタンスに、ドンドン連結していくだけです。
 文字列の+での連結にはご用心。このようにわかりやすくループ中にある場合以外も、それを含むメソッドが繰り返し呼ばれることだってあるわけです。どうせ同じことをやるのでしたら、「おーい、パソコンよ壊れちゃったのかぁ?」などと心配しなくてもいいように、処理2の方式で行きましょう。
<<前ページ目次次ページ>>
Copyright (c) 2004-2005 Nagi Imai All Rights Reserved..