Javaで大量のテキストを処理する機会があったので、文字列結合の効率化について復習しておこう。
環境&やること
- Java1.8
- JODEでデコンパイルして動きを見よう
文字列結合いろいろ
- "+"を使う
- String.concat()を使う
- StringBuffer/StringBuilderを使う
文字列結合
String a = "aaa";
String b = "bbb";
String str;
//"+"を使う
str = a + b;
//concatを使う
str = a.concat(b);
//StringBuilderを使う
str = new StringBuilder(a).append(b).toString();
concat()は新しくStringをnewして返すメソッド。
StringBuffer/Builderは可変長文字列を扱うクラスで、appendされた文字列をせっせとバッファに詰め込みます(いちいちnewしない)。
問題なのが"+"で、これはコンパイラの最適化によって書き換わります。使い方を間違えると大変。
ダメな例
早速やらかしてみましょう。
元
String str = "";
//やらかした例
for(int i = 0; i < 10000; i++){
str += a + b;
}
デコンパイル
String str = "";
for (int i = 0; i < 10000; i++)
str = new StringBuilder(str).append(a).append(b).toString();
StringBuilderを使った文字列結合は速いんですが、そもそもStringBuilderをnewすること自体はそれなりにコストがかかります。ループの中でnewしちゃだめです。
インスタンス使いまわす
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.setLength(0);
str = sb.append(str).append(a).append(b).toString();
}
定数の結合
もう一つ注意点。
定数は最適化の段階で一つにまとめてくれます。なので最速。最速というか結合じゃないし。
元
final String FA = "finalA";
final String FB = "finalB";
String a = "A";
String b = "B";
String str = "";
str = "A" + "B"; //文字列
str = a + b; //変数の結合
str = FA + FB; //定数の結合
str = a + FA + FB; //変数+定数
str = FA + FB + a; //定数+変数
デコンパイル
str = "AB";
str = new StringBuilder(a).append(b).toString();
str = "finalAfinalB";
str = new StringBuilder(a).append("finalA").append("finalB").toString();
str = new StringBuilder("finalAfinalB").append(a).toString();
ただ、定数と変数が混ざった下2行を見てみると、変数が混ざる前は結合されてますが、変数以降はappendになってますね。
まとめ
- ループ内での"+"を使った文字列結合は注意
- 定数と変数を混ぜる場合も注意
- そうは言っても何万回も繰り返さない処理なら可読性UPのために"+"使ってもいいよね
なんで今さら?
Q. Javaプログラマーにとっては何を今更という内容かもしれないが、なんで書いた?
A. 自分で書いたコード見直してたら思いっきりやらかしてたから。