文字列の結合っていろいろなやり方があるのですが、みなさんはどんなやり方をされていますか?
String firstName = "Junichi"; String lastName = "Kato"; String fullName = ""; fullName += firstName; fullName += lastName;
とか、もちろんやってないですよね?
+演算子ってわかって使わないとヒドイことになりますよw
性能をはかってみよう
ちょっと、簡単に以下のような性能をはかるテストを書いてみた。
文字列の連結方式としては、
- Stringの+演算子
- Stringのcontractメソッド
- StringBufferのappendメソッド
- StringBuilderのappendメソッド
が考えられるので、それらの性能をはかってみた。
package tests; import org.junit.Test; public class StringTest { @Test public void testPlusString() { String result = ""; long start = System.currentTimeMillis(); for (int i = 0; i < numItems(); i++) { result += lineForItem(i); } long finish = System.currentTimeMillis(); System.out.println(String.format("String + time = %d", finish - start)); } @Test public void testContractString() { String result = ""; long start = System.currentTimeMillis(); for (int i = 0; i < numItems(); i++) { result.concat(lineForItem(i)); } long finish = System.currentTimeMillis(); System.out.println(String.format("String#contract time = %d", finish - start)); } @Test public void testStringBufferAppendString() { StringBuffer result = new StringBuffer(); long start = System.currentTimeMillis(); for (int i = 0; i < numItems(); i++) { result.append(lineForItem(i)); } long finish = System.currentTimeMillis(); System.out.println(String.format("StringBuffer#append time = %d", finish - start)); } @Test public void testStringBuilderAppendString() { StringBuilder result = new StringBuilder(); long start = System.currentTimeMillis(); for (int i = 0; i < numItems(); i++) { result.append(lineForItem(i)); } long finish = System.currentTimeMillis(); System.out.println(String.format("StringBuilder#append time = %d", finish - start)); } private String lineForItem(int i) { return String.valueOf(i); } private int numItems() { return 10000; } }
ちなみに、上記の処理はEffictive Javaの項目51を参考にしている。
このテストは、10000回のループで次々に文字列を連結していくテスト。ループで数値の桁も繰り上がり連結する文字列も長くなっていく。
テスト結果
わたしの環境でのテスト結果はこちら。単位はmsec。
String + time = 593 String#contract time = 6 StringBuffer#append time = 3 StringBuilder#append time = 2
String#+ > String#contract > StringBuffer#append > StringBuilder#append
Stringの+はひどいことにw StringBuilder#appendの約297倍とか。
まだ、contractを使ったほうがマシ。
StringBufferと、StringBuilderは同期化のコストが若干ありそうですが、この程度なら気にするレベルではなさそうです。まぁ、同期化が必要ないところではStringBuilderが基本ということで。
Stringの+を使うと場合によっては、二つの文字列がコピーされてしまうようだ。。。Stringは不変オブジェクトだから仕方あるまいw
javacの+演算子の最適化
String a = "abc" + "def";
の場合はコンパイラの最適化で
String a = "abcdef";
に変換されるからよい。
String s = "def"; String str = "abc" + s;
は、
String s = "def"; String str = (new StringBuilder("abc")).append(s) .toString();
に最適化される。まぁ、これもよい。
String str = "abc"; str += "def"; str += "ghi";
は、
String str = "abc"; str = (new StringBuilder(String.valueOf(str))) .append("def").toString(); str = (new StringBuilder(String.valueOf(str))) .append("ghi").toString();
になってしまうらしいwww これ確実に死亡フラグですねw
Stringの+を使うところは、たとえば、一行のための処理とか、文字列が小さい場合の連結とかぐらいかな。それでも高頻度に実行される場合は避けるべき。このサイトでも、一行の処理ならよいが(というのはjavaコンパイラがStringBuilderのコードに最適化するから)、複数行やループ内ではStringBuilderを使わないと効率が悪いと言及されている。
まぁ、上記のような脳内変換をしたくない人は、極力、StringBuilderを使いましょう。
お仕事でプログラムする場合は絶対頭に入れておいてくださいwww