先日、静的な文字列に対するJava演算子の振る舞いについて注意を受けたので、今更ながらメモを残しておく。
下記のコード1とコード2がどのようにコンパイルされるか。
コード1 : aからzまでのアルファベットを「+」演算して結合文字列変数を作成する。
public class StringTest1 {
public static void main(String[] args) {
String str = "a" + "b" + "c" + "d" + "e" + "f" + "g" + "h" + "i" + "j" + "k" + "l" + "m" + "n" + "o" + "p" + "q" + "r" + "s" + "t" + "r" + "u" + "v" + "w" + "x" + "y" + "z";
System.out.println(str);
}
}
コード2 : aからzまでのアルファベットを一つの文字列変数として記述する。
public class StringTest2 {
public static void main(String[] args) {
String str = "abcdefghijklmnopqrstruvwxyz";
System.out.println(str);
}
}
コンパイル後のクラスのファイルサイズ
-rw-r--r-- 1 user group 449 Jul 7 17:42 StringTest1.class -rw-r--r-- 1 user group 449 Jul 7 17:42 StringTest2.class
コード1の逆アセンブル
$ javap StringTest1.class
Compiled from "StringTest1.java"
public class StringTest1 {
public StringTest1();
public static void main(java.lang.String[]);
}
コード2の逆アセンブル
$ javap StringTest2.class
Compiled from "StringTest2.java"
public class StringTest2 {
public StringTest2();
public static void main(java.lang.String[]);
}
コンパイル後のコード1とコード2は同じものであることがわかる。
次に、「+」演算子をStringBuilderのappendメソッドに変更して、実行時間を計測する。
コード1 改 : コード1で実行時間を計測するようにした。
public class StringTest1 {
public static void main(String[] args) {
long startTime = System.nanoTime();
String str = "a" + "b" + "c" + "d" + "e" + "f" + "g" + "h" + "i" + "j" + "k" + "l" + "m" + "n" + "o" + "p" + "q" + "r" + "s" + "t" + "r" + "u" + "v" + "w" + "x" + "y" + "z";
System.out.println(str);
long endTime = System.nanoTime();
System.out.println(endTime - startTime);
}
}
コード3 : コード1改の「+」演算子をStringBuilderのappendメソッドに変更した。
public class StringTest3 {
public static void main(String[] args) {
long startTime = System.nanoTime();
StringBuilder build = new StringBuilder("a")
.append("b").append("c").append("d").append("e").append("f")
.append("g").append("h").append("i").append("j").append("k")
.append("l").append("m").append("n").append("o").append("p")
.append("q").append("r").append("s").append("t").append("u")
.append("v").append("w").append("x").append("y").append("z");
System.out.println(build);
long endTime = System.nanoTime();
System.out.println(endTime - startTime);
}
}
コード1実行:
$ java StringTest1 abcdefghijklmnopqrstruvwxyz 178237
コード3実行:
$ java StringTest3 abcdefghijklmnopqrstuvwxyz 248851
StringBuilderを使用するとかえって遅くなることがわかる。
実行時に動的に変化しない文字列変数に関しては、コンパイラが最適化してくれるので、「+」演算子を除去しても意味がなく、StringBuilderを使用するとかえって効率が悪くなる。