先日、静的な文字列に対する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を使用するとかえって効率が悪くなる。