「+」演算子での文字列連結は、コンパイラでStringBuilder#append()にされるよ!
文字列をちょこっと連結したい時に、いちいちStringBuilderを使うのは面倒くさいですよねー。
というか、「+」で連結しても実はコンパイラがうまいこと最適化してくれるんじゃね?とふと思い立って調べて見ました。
結論:単純な連結なら、コンパイラがちゃんとStringBuilderにしてくれます。
↓のような1ステートメントに収まる単純な文字列連結であれば、コンパイラがちゃんとStringBuilderに変換してくれます。
// リテラルの連結 String a1() { return "a" + "b"; } String a2() { return "a" + "b" + "c"; } String a3() { return "a" + 10; } String a4() { return 10 + "a"; } String a5() { return "a" + 10 + "b"; } String a6() { return "a" + true; } String a7() { return false + "a"; } String a8() { return "a" + false + "b"; } String a9() { return new Object() + "a"; } // 引数、メソッドの評価結果 String b1( String x ) { return "a" + x; } String b2() { return "a" + String.valueOf(10); } String b3() { return "a" + b2(); } String b4() { return a1() + b2(); }
javapの出力結果は以下。ちゃんとStringBuilder#appendを使うようにコンパイル時に変換されています。
java.lang.String a1(); Code: 0: ldc #2; //String ab 2: areturn java.lang.String a2(); Code: 0: ldc #3; //String abc 2: areturn java.lang.String a3(); Code: 0: ldc #4; //String a10 2: areturn java.lang.String a4(); Code: 0: ldc #5; //String 10a 2: areturn java.lang.String a5(); Code: 0: ldc #6; //String a10b 2: areturn java.lang.String a6(); Code: 0: ldc #7; //String atrue 2: areturn java.lang.String a7(); Code: 0: ldc #8; //String falsea 2: areturn java.lang.String a8(); Code: 0: ldc #9; //String afalseb 2: areturn java.lang.String a9(); Code: 0: new #10; //class java/lang/StringBuilder 3: dup 4: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 7: new #12; //class java/lang/Object 10: dup 11: invokespecial #1; //Method java/lang/Object."<init>":()V 14: invokevirtual #13; //Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 17: ldc #14; //String a 19: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 25: areturn java.lang.String b1(java.lang.String); Code: 0: new #10; //class java/lang/StringBuilder 3: dup 4: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 7: ldc #14; //String a 9: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12: aload_1 13: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 19: areturn java.lang.String b2(); Code: 0: new #10; //class java/lang/StringBuilder 3: dup 4: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 7: ldc #14; //String a 9: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12: bipush 10 14: invokestatic #17; //Method java/lang/String.valueOf:(I)Ljava/lang/String; 17: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 23: areturn java.lang.String b3(); Code: 0: new #10; //class java/lang/StringBuilder 3: dup 4: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 7: ldc #14; //String a 9: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12: aload_0 13: invokevirtual #18; //Method b2:()Ljava/lang/String; 16: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: areturn java.lang.String b4(); Code: 0: new #10; //class java/lang/StringBuilder 3: dup 4: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 7: aload_0 8: invokevirtual #19; //Method a1:()Ljava/lang/String; 11: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: aload_0 15: invokevirtual #18; //Method b2:()Ljava/lang/String; 18: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24: areturn
最適化されないパターン
うまく最適化されなかったパターンは以下。試した範囲では、連結を複数のステートメントに別けて行なうとダメみたいですね。複数のStringBuilderに分割されてしまっています。
// 複数のステートメントに別けて連結 String c1() { String str = "a"; str = str + "b"; str = str + "c"; str = str + "d"; return str; } String c2() { String str = "a"; str += "b"; str += "c"; str += "d"; return str; }
javapの出力結果です。
java.lang.String c1(); Code: 0: ldc #14; //String a 2: astore_1 3: new #10; //class java/lang/StringBuilder 6: dup 7: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #20; //String b 16: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: new #10; //class java/lang/StringBuilder 26: dup 27: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 30: aload_1 31: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: ldc #21; //String c 36: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 42: astore_1 43: new #10; //class java/lang/StringBuilder 46: dup 47: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 50: aload_1 51: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 54: ldc #22; //String d 56: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 59: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 62: astore_1 63: aload_1 64: areturn java.lang.String c2(); Code: 0: ldc #14; //String a 2: astore_1 3: new #10; //class java/lang/StringBuilder 6: dup 7: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #20; //String b 16: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: new #10; //class java/lang/StringBuilder 26: dup 27: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 30: aload_1 31: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: ldc #21; //String c 36: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 42: astore_1 43: new #10; //class java/lang/StringBuilder 46: dup 47: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 50: aload_1 51: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 54: ldc #22; //String d 56: invokevirtual #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 59: invokevirtual #16; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 62: astore_1 63: aload_1 64: areturn
まとめ
String str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; str += "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
ではなく、
String str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
と書くとちょっと早い。(ただし、よほどクリティカルな部分でない限り無視していいレベルと思われますが・・・。)
これで内なる原理主義者との葛藤から、少しは解放されますね!