Javaの文字列の結合は、+
演算子が良いのか、StringBuilder
(or StringBuffer
)が良いのか、これもよく聞く問題です
先に結論言っちゃうと、コンパイル時に最適化されるので、+
演算子を積極的に使うと良いです
むしろ、StringBuilder
にしてると最適化が効かず効率が悪いこともあります
とは言え、全ての場面で+
演算子を使えばいいかと言うとそうでもないんですよねぇ~
StringBuilder
を使う方が良い場面もあります
この記事では、javap.exeを使って、文字連結時に使う+
演算子とStringBuilder
の使い分け方の根本的な考え方を示します
ちょっと記事の文量が多いように見えますが、読むべき量は意外と少ないので、良かったら最後まで見てあげてください
StringBuilder
とStringBuffer
についてStringBuilder
もStringBuffer
も使い方は同じですしそれほど大きな違いは無いため、本記事ではStringBuilder
のみを取り上げます
ちなみに、StringBuilder
とStringBuffer
との違いは
StringBuilder
スレッド処理なし
処理早いStringBuffer
スレッド処理あり
処理遅い
現実的にはスレッド云々は別途対応して、StringBuilder
を使えばいいのかなと思います
※StringBuffer
を使うメリットがあまり無い?
- Javaの文字列の結合は
+
演算子さえ使っておけばいいと思っている人 - Javaの文字列の結合は
StringBuilder
クラスさえ使っておけばいいと思っている人 - Javaの文字列の結合の事なんて考えたこと無かった人
- Javaの知識必要
- JDKをインストールしている
◆解決できるかも知れないお悩み
- Javaの文字列結合において、
+
演算子とStringBuilder
クラスの使い分け方が理解できます
◆どうやって解決するか
- javap.exeを使って、ソースコードの展開方法(バイトコード)を見ます
Contents
まずは結論! +
演算子とStringBuilder
クラスとの使い分け方
- 基本的には
+
演算子を使えばいいです - 但し、1行で全部の文字列を連結する場合のみ
- 何行かに分けて連結する場合は、
StringBuilder
クラスを使ってね for
とかwhile
の中ではStringBuilder
クラスを使う方が良いでしょう- どっちを使えばいいか分からない場合は、javap.exeで確認せよ!
javapを使ってみよう
javaファイルをコンパイルすると、バイトコードというものに展開されます
バイトコードに展開される内容はソースコードと全く同じというわけではなく最適化されることがありますし使用している命令が変わることもあります
Javaの実行環境であるJVMはそのバイトコードを読み取って実行してくれます
ですので、実際にどのようなコードで動くのかはバイトコードを見るのが良いのです
で、そのバイトコードがclassファイルです
※classファイルはバイトコード以外の情報も持ってるよ
javapを使えばclassファイルに入っているバイトコードが見られます
まずはjavapの存在を確認
JDKをインストールした時のディレクトリのbinフォルダにjavapはいます
僕の環境だとここ↓
- C:\Program Files\Java\jdk1.8.0_72\bin\javap.exe
WindowsでもMacでもPATHを通していると思うので、以下のコマンドを打ってみてください
> javap -help
↓こんな感じで何か表示されればOKです
>javap -help 使用方法: javap使用可能なオプションには次のものがあります: -help --help -? この使用方法のメッセージを出力する -version バージョン情報 -v -verbose 追加情報を出力する -l 行番号とローカル変数表を出力する -public publicクラスおよびメンバーのみを表示する -protected protected/publicクラスおよびメンバーのみを表示する -package package/protected/publicクラスおよび メンバーのみを表示する(デフォルト) -p -private すべてのクラスとメンバーを表示する -c コードを逆アセンブルする -s 内部タイプ署名を出力する -sysinfo 処理しているクラスのシステム情報(パス、サイズ、日付、MD5ハッシュ) を表示する -constants final定数を表示する -classpath ユーザー・クラス・ファイルを検索する場所を指定する -cp ユーザー・クラス・ファイルを検索する場所を指定する -bootclasspath ブートストラップ・クラス・ファイルの場所をオーバーライドする
以下のソースコードをコンパイルします
このソースコードをコンパイルしてclassファイルを作ってちょーーーだい!
Main.javaは別にコンパイルしなくてもいいですよ
動かしたいのであればコンパイルすればいいですが、SamplePlus.javaとSampleBuilder.javaさえコンパイルしてもらえればOKです
public class Main { public static void main(String argv[]) { SamplePlus sp = new SamplePlus(); System.out.println( "Test11 : " + sp.Test11() ); System.out.println( "Test12 : " + sp.Test12() ); System.out.println( "Test13 : " + sp.Test13() ); System.out.println( "Test14 : " + sp.Test14() ); System.out.println( "Test21 : " + sp.Test21() ); System.out.println( "Test22 : " + sp.Test22() ); System.out.println( "Test23 : " + sp.Test23() ); System.out.println( "Test24 : " + sp.Test24() ); System.out.println( "Test25 : " + sp.Test25() ); System.out.println( "Test26 : " + sp.Test26() ); SampleBuilder sb = new SampleBuilder(); System.out.println( "Test11 : " + sb.Test11() ); System.out.println( "Test12 : " + sb.Test12() ); System.out.println( "Test22 : " + sb.Test22() ); System.out.println( "Test24 : " + sb.Test24() ); System.out.println( "Test26 : " + sb.Test26() ); } }
public class SamplePlus { //=========================================================== // ノーマル系 //=========================================================== //*********************************************************** // 大分類:1行で連結 // 中分類:String変数使用 // 小分類:--- //*********************************************************** public static String Test11() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; String gabo; gabo = hoge + fuga + piyo; return gabo; } //*********************************************************** // 大分類:1行で連結 // 中分類:文字列使用 // 小分類:--- //*********************************************************** public static String Test12() { String gabo; gabo = "aiueo" + "kakikukeko" + "sasisuseso"; return gabo; } //*********************************************************** // 大分類:複数行で連結 // 中分類:String変数使用 // 小分類:--- //*********************************************************** public static String Test13() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; String gabo; gabo = ""; gabo += hoge; gabo += fuga; gabo += piyo; return gabo; } //*********************************************************** // 大分類:複数行で連結 // 中分類:文字列使用 // 小分類:--- //*********************************************************** public static String Test14() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; String gabo; gabo = ""; gabo += "aiueo"; gabo += "kakikukeko"; gabo += "sasisuseso"; return gabo; } //*********************************************************** // 大分類:1行で連結 // 中分類:String変数と文字列の混合使用 // 小分類:--- //*********************************************************** public static String Test15() { String hoge = "aiueo"; String piyo = "sasisuseso"; String gabo; gabo = hoge + "kakikukeko" + piyo + "tatituteto"; return gabo; } //=========================================================== // ループ系 //=========================================================== //*********************************************************** // 大分類:ループ使用 // 中分類:String変数使用 // 小分類:ループの【外】でString変数定義 // :代入系 //*********************************************************** public static String Test21() { String hoge = "aiueo"; String gabo = ""; for(int i=0; i < 100; i++) { gabo = hoge; } return gabo; } //*********************************************************** // 大分類:ループ使用 // 中分類:String変数使用 // 小分類:ループの【外】でString変数定義 // :足していく系 //*********************************************************** public static String Test22() { String hoge = "aiueo"; String gabo = ""; for(int i=0; i < 100; i++) { gabo += hoge; } return gabo; } //*********************************************************** // 大分類:ループ使用 // 中分類:String変数使用 // 小分類:ループの【中】でString変数定義 // :代入系 //*********************************************************** public static String Test23() { String gabo = ""; for(int i=0; i < 100; i++) { String hoge = "aiueo"; gabo = hoge; } return gabo; } //*********************************************************** // 大分類:ループ使用 // 中分類:String変数使用 // 小分類:ループの【中】でString変数定義 // :足していく系 //*********************************************************** public static String Test24() { String gabo = ""; for(int i=0; i < 100; i++) { String hoge = "aiueo"; gabo += hoge; } return gabo; } //*********************************************************** // 大分類:ループ使用 // 中分類:文字列使用 // 小分類:--- // :代入系 //*********************************************************** public static String Test25() { String gabo = ""; for(int i=0; i < 100; i++) { gabo = "aiueo"; } return gabo; } //*********************************************************** // 大分類:ループ使用 // 中分類:文字列使用 // 小分類:--- // :足していく系 //*********************************************************** public static String Test26() { String gabo = ""; for(int i=0; i < 100; i++) { gabo += "aiueo"; } return gabo; } }
public class SampleBuilder { //=========================================================== // ノーマル系 //=========================================================== //*********************************************************** // 大分類:1行で連結 // 中分類:String変数使用 // 小分類:--- //*********************************************************** public static String Test11() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; StringBuilder gabo = new StringBuilder(); gabo.append(hoge); gabo.append(fuga); gabo.append(piyo); return gabo.toString(); } //*********************************************************** // 大分類:1行で連結 // 中分類:文字列使用 // 小分類:--- //*********************************************************** public static String Test12() { StringBuilder gabo = new StringBuilder(); gabo.append("aiueo"); gabo.append("kakikukeko"); gabo.append("sasisuseso"); return gabo.toString(); } // //*********************************************************** // // 大分類:複数行で連結 // // 中分類:String変数使用 // // 小分類:--- // //*********************************************************** // public static String Test13() { // // } // //*********************************************************** // // 大分類:複数行で連結 // // 中分類:文字列使用 // // 小分類:--- // //*********************************************************** // public static String Test14() { // // } //*********************************************************** // 大分類:1行で連結 // 中分類:String変数と文字列の混合使用 // 小分類:--- //*********************************************************** public static String Test15() { String hoge = "aiueo"; String piyo = "sasisuseso"; StringBuilder gabo = new StringBuilder(); gabo.append(hoge); gabo.append("kakikukeko"); gabo.append(piyo); gabo.append("tatituteto"); return gabo.toString(); } //=========================================================== // ループ系 //=========================================================== // //*********************************************************** // // 大分類:ループ使用 // // 中分類:String変数使用 // // 小分類:ループの【外】でString変数定義 // // :代入系 // //*********************************************************** // public static String Test21() { // // } //*********************************************************** // 大分類:ループ使用 // 中分類:String変数使用 // 小分類:ループの【外】でString変数定義 // :足していく系 //*********************************************************** public static String Test22() { String hoge = "aiueo"; StringBuilder gabo = new StringBuilder(); for(int i=0; i < 100; i++) { gabo.append(hoge); } return gabo.toString(); } // //*********************************************************** // // 大分類:ループ使用 // // 中分類:String変数使用 // // 小分類:ループの【中】でString変数定義 // // :代入系 // //*********************************************************** // public static String Test23() { // } //*********************************************************** // 大分類:ループ使用 // 中分類:String変数使用 // 小分類:ループの【中】でString変数定義 // :足していく系 //*********************************************************** public static String Test24() { StringBuilder gabo = new StringBuilder(); for(int i=0; i < 100; i++) { String hoge = "aiueo"; gabo.append(hoge); } return gabo.toString(); } // //*********************************************************** // // 大分類:ループ使用 // // 中分類:文字列使用 // // 小分類:--- // // :代入系 // //*********************************************************** // public static String Test25() { // // } //*********************************************************** // 大分類:ループ使用 // 中分類:文字列使用 // 小分類:--- // :足していく系 //*********************************************************** public static String Test26() { StringBuilder gabo = new StringBuilder(); for(int i=0; i < 100; i++) { gabo.append("aiueo"); } return gabo.toString(); } }
javapでclassファイルを読み込みます
> javap -c SamplePlus.class > javap -c SampleBuilder.class
テキストファイルに出力したければリダイレクトしてください
> javap -c SamplePlus.class > SamplePlus_javap.txt > javap -c SampleBuilder.class > SampleBuilder_javap.txt
こんなデータが出力されます
↓これがバイトコードです
Compiled from "SamplePlus.java" public class SamplePlus { public SamplePlus(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return public static java.lang.String Test11(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder." ":()V 16: aload_0 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: aload_1 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: aload_2 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: astore_3 32: aload_3 33: areturn public static java.lang.String Test12(); Code: 0: ldc #9 // String aiueokakikukekosasisuseso 2: astore_0 3: aload_0 4: areturn public static java.lang.String Test13(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: ldc #10 // String 11: astore_3 12: new #5 // class java/lang/StringBuilder 15: dup 16: invokespecial #6 // Method java/lang/StringBuilder." ":()V 19: aload_3 20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: aload_0 24: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 30: astore_3 31: new #5 // class java/lang/StringBuilder 34: dup 35: invokespecial #6 // Method java/lang/StringBuilder." ":()V 38: aload_3 39: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 42: aload_1 43: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 46: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 49: astore_3 50: new #5 // class java/lang/StringBuilder 53: dup 54: invokespecial #6 // Method java/lang/StringBuilder." ":()V 57: aload_3 58: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 61: aload_2 62: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 65: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 68: astore_3 69: aload_3 70: areturn public static java.lang.String Test14(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: ldc #10 // String 11: astore_3 12: new #5 // class java/lang/StringBuilder 15: dup 16: invokespecial #6 // Method java/lang/StringBuilder." ":()V 19: aload_3 20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: ldc #2 // String aiueo 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: astore_3 32: new #5 // class java/lang/StringBuilder 35: dup 36: invokespecial #6 // Method java/lang/StringBuilder." ":()V 39: aload_3 40: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 43: ldc #3 // String kakikukeko 45: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 48: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 51: astore_3 52: new #5 // class java/lang/StringBuilder 55: dup 56: invokespecial #6 // Method java/lang/StringBuilder." ":()V 59: aload_3 60: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 63: ldc #4 // String sasisuseso 65: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 68: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 71: astore_3 72: aload_3 73: areturn public static java.lang.String Test15(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #4 // String sasisuseso 5: astore_1 6: new #5 // class java/lang/StringBuilder 9: dup 10: invokespecial #6 // Method java/lang/StringBuilder." ":()V 13: aload_0 14: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: ldc #3 // String kakikukeko 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: aload_1 23: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: ldc #11 // String tatituteto 28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: astore_2 35: aload_2 36: areturn public static java.lang.String Test21(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #10 // String 5: astore_1 6: iconst_0 7: istore_2 8: iload_2 9: bipush 100 11: if_icmpge 22 14: aload_0 15: astore_1 16: iinc 2, 1 19: goto 8 22: aload_1 23: areturn public static java.lang.String Test22(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #10 // String 5: astore_1 6: iconst_0 7: istore_2 8: iload_2 9: bipush 100 11: if_icmpge 39 14: new #5 // class java/lang/StringBuilder 17: dup 18: invokespecial #6 // Method java/lang/StringBuilder." ":()V 21: aload_1 22: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: aload_0 26: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: astore_1 33: iinc 2, 1 36: goto 8 39: aload_1 40: areturn public static java.lang.String Test23(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 22 11: ldc #2 // String aiueo 13: astore_2 14: aload_2 15: astore_0 16: iinc 1, 1 19: goto 5 22: aload_0 23: areturn public static java.lang.String Test24(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 39 11: ldc #2 // String aiueo 13: astore_2 14: new #5 // class java/lang/StringBuilder 17: dup 18: invokespecial #6 // Method java/lang/StringBuilder." ":()V 21: aload_0 22: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: aload_2 26: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: astore_0 33: iinc 1, 1 36: goto 5 39: aload_0 40: areturn public static java.lang.String Test25(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 20 11: ldc #2 // String aiueo 13: astore_0 14: iinc 1, 1 17: goto 5 20: aload_0 21: areturn public static java.lang.String Test26(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 37 11: new #5 // class java/lang/StringBuilder 14: dup 15: invokespecial #6 // Method java/lang/StringBuilder." ":()V 18: aload_0 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: ldc #2 // String aiueo 24: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 30: astore_0 31: iinc 1, 1 34: goto 5 37: aload_0 38: areturn }
Compiled from "SampleBuilder.java" public class SampleBuilder { public SampleBuilder(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return public static java.lang.String Test11(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder." ":()V 16: astore_3 17: aload_3 18: aload_0 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: pop 23: aload_3 24: aload_1 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: pop 29: aload_3 30: aload_2 31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: pop 35: aload_3 36: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 39: areturn public static java.lang.String Test12(); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder." ":()V 7: astore_0 8: aload_0 9: ldc #2 // String aiueo 11: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: pop 15: aload_0 16: ldc #3 // String kakikukeko 18: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: pop 22: aload_0 23: ldc #4 // String sasisuseso 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: pop 29: aload_0 30: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 33: areturn public static java.lang.String Test15(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #4 // String sasisuseso 5: astore_1 6: new #5 // class java/lang/StringBuilder 9: dup 10: invokespecial #6 // Method java/lang/StringBuilder." ":()V 13: astore_2 14: aload_2 15: aload_0 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: pop 20: aload_2 21: ldc #3 // String kakikukeko 23: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: pop 27: aload_2 28: aload_1 29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: pop 33: aload_2 34: ldc #9 // String tatituteto 36: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: pop 40: aload_2 41: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 44: areturn public static java.lang.String Test22(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: new #5 // class java/lang/StringBuilder 6: dup 7: invokespecial #6 // Method java/lang/StringBuilder." ":()V 10: astore_1 11: iconst_0 12: istore_2 13: iload_2 14: bipush 100 16: if_icmpge 31 19: aload_1 20: aload_0 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: pop 25: iinc 2, 1 28: goto 13 31: aload_1 32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: areturn public static java.lang.String Test24(); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder." ":()V 7: astore_0 8: iconst_0 9: istore_1 10: iload_1 11: bipush 100 13: if_icmpge 31 16: ldc #2 // String aiueo 18: astore_2 19: aload_0 20: aload_2 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: pop 25: iinc 1, 1 28: goto 10 31: aload_0 32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: areturn public static java.lang.String Test26(); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder." ":()V 7: astore_0 8: iconst_0 9: istore_1 10: iload_1 11: bipush 100 13: if_icmpge 29 16: aload_0 17: ldc #2 // String aiueo 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: pop 23: iinc 1, 1 26: goto 10 29: aload_0 30: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 33: areturn }
javapで出力された内容を解説します(SamplePlus編)
注目して欲しいのは2つ
- newの数
+
演算子がStringBuilder
に変換(最適化)されている
+
演算子での文字連結は基本的にはStringBuilder
に変換されるのです
ですので、+
演算子を積極的に使うことを推奨します
StringBuilder
より+
演算子の方が読みやすいですよね
でもですね、newの数が無駄に多くなることがあります
その場合はインスタンス生成→解放の処理が無駄なので、素直にStringBuilder
を使う方が良い場面もあります
+
演算子で良い場合、悪い場合を見ていきましょう!
invokespecial:コンストラクタの呼び出し
invokevirtual:メソッドの呼び出し
ノーマル系
Test11
前置き
- 1行で連結
- String変数使用
public static String Test11() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; String gabo; gabo = hoge + fuga + piyo; return gabo; }
public static java.lang.String Test11(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder."":()V 16: aload_0 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: aload_1 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: aload_2 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: astore_3 32: aload_3 33: areturn
解説
この1行で連結する形は、+
演算子でいいです
Test12
- 1行で連結
- 文字列使用
public static String Test12() { String gabo; gabo = "aiueo" + "kakikukeko" + "sasisuseso"; return gabo; }
public static java.lang.String Test12(); Code: 0: ldc #9 // String aiueokakikukekosasisuseso 2: astore_0 3: aload_0 4: areturn
これも1行で連結する形ですね
しかも、#9のところを見れば分かるように、コンパイル時点でaiueokakikukekosasisuseso
を準備してくれてます
なので+
演算子でいいです
Test13
- 複数行で連結
- String変数使用
public static String Test13() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; String gabo; gabo = ""; gabo += hoge; gabo += fuga; gabo += piyo; return gabo; }
public static java.lang.String Test13(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: ldc #10 // String 11: astore_3 12: new #5 // class java/lang/StringBuilder 15: dup 16: invokespecial #6 // Method java/lang/StringBuilder."":()V 19: aload_3 20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: aload_0 24: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 30: astore_3 31: new #5 // class java/lang/StringBuilder 34: dup 35: invokespecial #6 // Method java/lang/StringBuilder." ":()V 38: aload_3 39: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 42: aload_1 43: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 46: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 49: astore_3 50: new #5 // class java/lang/StringBuilder 53: dup 54: invokespecial #6 // Method java/lang/StringBuilder." ":()V 57: aload_3 58: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 61: aload_2 62: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 65: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 68: astore_3 69: aload_3 70: areturn
Test11()を複数行で連結にした形です
複数行にするとnewを3個も呼び出すので、無駄になります
1行で連結するか、StringBuilder
を使いましょう
Test14
- 複数行で連結
- 文字列使用
public static String Test14() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; String gabo; gabo = ""; gabo += "aiueo"; gabo += "kakikukeko"; gabo += "sasisuseso"; return gabo; }
public static java.lang.String Test14(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: ldc #10 // String 11: astore_3 12: new #5 // class java/lang/StringBuilder 15: dup 16: invokespecial #6 // Method java/lang/StringBuilder."":()V 19: aload_3 20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: ldc #2 // String aiueo 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: astore_3 32: new #5 // class java/lang/StringBuilder 35: dup 36: invokespecial #6 // Method java/lang/StringBuilder." ":()V 39: aload_3 40: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 43: ldc #3 // String kakikukeko 45: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 48: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 51: astore_3 52: new #5 // class java/lang/StringBuilder 55: dup 56: invokespecial #6 // Method java/lang/StringBuilder." ":()V 59: aload_3 60: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 63: ldc #4 // String sasisuseso 65: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 68: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 71: astore_3 72: aload_3 73: areturn
これもTest12()を複数行で連結にした形です
先ほどと同じように複数行にするとnewを3個も呼び出すので、無駄になります
1行で連結するか、StringBuilder
を使いましょう
Test15
- 1行で連結
- String変数と文字列の混合使用
public static String Test15() { String hoge = "aiueo"; String piyo = "sasisuseso"; String gabo; gabo = hoge + "kakikukeko" + piyo + "tatituteto"; return gabo; }
public static java.lang.String Test15(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #4 // String sasisuseso 5: astore_1 6: new #5 // class java/lang/StringBuilder 9: dup 10: invokespecial #6 // Method java/lang/StringBuilder."":()V 13: aload_0 14: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: ldc #3 // String kakikukeko 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: aload_1 23: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: ldc #11 // String tatituteto 28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: astore_2 35: aload_2 36: areturn
変数と文字列が混在していますが、1行連結ですので、newも1個ですね
+
演算子で問題ありません
ループ系
Test21
- ループ使用
- String変数使用
- ループの【外】でString変数定義
- 代入系
public static String Test21() { String hoge = "aiueo"; String gabo = ""; for(int i=0; i < 100; i++) { gabo = hoge; } return gabo; }
public static java.lang.String Test21(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #10 // String 5: astore_1 6: iconst_0 7: istore_2 8: iload_2 9: bipush 100 11: if_icmpge 22 14: aload_0 15: astore_1 16: iinc 2, 1 19: goto 8 22: aload_1 23: areturn
hogeをgoboへ100回代入してますけど、意味はないです
19:
で8:
へgotoしているので、9:
~16:
がfor
文の処理です
特にnewしているわけでもないので、問題ないですね
+
演算子の使用でOKです
Test22
- ループ使用
- String変数使用
- ループの【外】でString変数定義
- 足していく系
public static String Test22() { String hoge = "aiueo"; String gabo = ""; for(int i=0; i < 100; i++) { gabo += hoge; } return gabo; }
public static java.lang.String Test22(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #10 // String 5: astore_1 6: iconst_0 7: istore_2 8: iload_2 9: bipush 100 11: if_icmpge 39 14: new #5 // class java/lang/StringBuilder 17: dup 18: invokespecial #6 // Method java/lang/StringBuilder."":()V 21: aload_1 22: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: aload_0 26: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: astore_1 33: iinc 2, 1 36: goto 8 39: aload_1 40: areturn
Test21()を代入から常に連結に変えたものです
for
文の処理はどこからどこまででしょうか?
36:
でgotoしているので、9:
~33:
がfor
文の処理ですね
ということは、毎回newしちゃってます
無駄ですね...
連結しまくるなら、StringBuilder
を使いましょう
Test23
- ループ使用
- String変数使用
- ループの【中】でString変数定義
- 代入系
public static String Test23() { String gabo = ""; for(int i=0; i < 100; i++) { String hoge = "aiueo"; gabo = hoge; } return gabo; }
public static java.lang.String Test23(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 22 11: ldc #2 // String aiueo 13: astore_2 14: aload_2 15: astore_0 16: iinc 1, 1 19: goto 5 22: aload_0 23: areturn
Test21()の変数hogeの宣言位置がfor
文の中に移動しました
しかし、newしていませんね
これは"aiueo"の値固定なので、11:
で準備してくれてますね
+
演算子でいいでしょう
Test24
- ループ使用
- String変数使用
- ループの【中】でString変数定義
- 足していく系
public static String Test24() { String gabo = ""; for(int i=0; i < 100; i++) { String hoge = "aiueo"; gabo += hoge; } return gabo; }
public static java.lang.String Test24(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 39 11: ldc #2 // String aiueo 13: astore_2 14: new #5 // class java/lang/StringBuilder 17: dup 18: invokespecial #6 // Method java/lang/StringBuilder."":()V 21: aload_0 22: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: aload_2 26: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32: astore_0 33: iinc 1, 1 36: goto 5 39: aload_0 40: areturn
Test22()の変数hogeの宣言位置がfor
文の中に移動しましたが、形は殆ど変わりません
for
文の中で毎回newしています
StringBuilder
を使いましょう
StringBuilder
の宣言はfor
文の外でしましょう
Test25
- ループ使用
- 文字列使用
- ---
- 代入系
public static String Test25() { String gabo = ""; for(int i=0; i < 100; i++) { gabo = "aiueo"; } return gabo; }
public static java.lang.String Test25(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 20 11: ldc #2 // String aiueo 13: astore_0 14: iinc 1, 1 17: goto 5 20: aload_0 21: areturn
Test21()での変数hogeが直接"aiueo"の文字列になりました
最適化内容はほぼ同じ
+
演算子の使用でOKです
Test26
- ループ使用
- 文字列使用
- ---
- 足していく系
public static String Test26() { String gabo = ""; for(int i=0; i < 100; i++) { gabo += "aiueo"; } return gabo; }
public static java.lang.String Test26(); Code: 0: ldc #10 // String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 37 11: new #5 // class java/lang/StringBuilder 14: dup 15: invokespecial #6 // Method java/lang/StringBuilder."":()V 18: aload_0 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: ldc #2 // String aiueo 24: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 30: astore_0 31: iinc 1, 1 34: goto 5 37: aload_0 38: areturn
Test22()での変数hogeが直接"aiueo"の文字列になりました
最適化内容はほぼ同じで、毎回newしちゃってます
無駄ですね...
連結しまくるなら、StringBuilder
を使いましょう
javapで出力された内容を解説します(SampleBuilder編)
つぎに、StringBuilder
を使った場合を見ます
バイトコードに展開されてもStringBuilder
なので、ソースコードとバイトコードに大きな違いは出ませんが、一応見てみましょう
注目して欲しいのはこれ
- newの数
invokespecial:コンストラクタの呼び出し
invokevirtual:メソッドの呼び出し
ノーマル系
Test11
前置き
- 1行で連結
- String変数使用
public static String Test11() { String hoge = "aiueo"; String fuga = "kakikukeko"; String piyo = "sasisuseso"; StringBuilder gabo = new StringBuilder(); gabo.append(hoge); gabo.append(fuga); gabo.append(piyo); return gabo.toString(); }
public static java.lang.String Test11(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #3 // String kakikukeko 5: astore_1 6: ldc #4 // String sasisuseso 8: astore_2 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder."":()V 16: astore_3 17: aload_3 18: aload_0 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: pop 23: aload_3 24: aload_1 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: pop 29: aload_3 30: aload_2 31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: pop 35: aload_3 36: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 39: areturn
解説
+
演算子の時よりも少し処理が多いですね
やっぱり1行で連結する形は、+
演算子がいいですね
Test12
- 1行で連結
- 文字列使用
public static String Test12() { StringBuilder gabo = new StringBuilder(); gabo.append("aiueo"); gabo.append("kakikukeko"); gabo.append("sasisuseso"); return gabo.toString(); }
public static java.lang.String Test12(); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder."":()V 7: astore_0 8: aload_0 9: ldc #2 // String aiueo 11: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: pop 15: aload_0 16: ldc #3 // String kakikukeko 18: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: pop 22: aload_0 23: ldc #4 // String sasisuseso 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: pop 29: aload_0 30: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 33: areturn
これもTest11()と同じく、+
演算子の時よりも少し処理が多くなっていますね
やっぱり1行で連結する形は、+
演算子がいいですね
Test13
※無し
Test14
※無し
Test15
- 1行で連結
- String変数と文字列の混合使用
public static String Test15() { String hoge = "aiueo"; String piyo = "sasisuseso"; StringBuilder gabo = new StringBuilder(); gabo.append(hoge); gabo.append("kakikukeko"); gabo.append(piyo); gabo.append("tatituteto"); return gabo.toString(); }
public static java.lang.String Test15(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: ldc #4 // String sasisuseso 5: astore_1 6: new #5 // class java/lang/StringBuilder 9: dup 10: invokespecial #6 // Method java/lang/StringBuilder."":()V 13: astore_2 14: aload_2 15: aload_0 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: pop 20: aload_2 21: ldc #3 // String kakikukeko 23: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: pop 27: aload_2 28: aload_1 29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: pop 33: aload_2 34: ldc #9 // String tatituteto 36: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: pop 40: aload_2 41: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 44: areturn
これもTest11()と同じく、+
演算子の時よりも少し処理が多くなっていますね
やっぱり1行で連結する形は、+
演算子がいいですね
ループ系
Test21
※無し
Test22
- ループ使用
- String変数使用
- ループの【外】でString変数定義
- 足していく系
public static String Test22() { String hoge = "aiueo"; StringBuilder gabo = new StringBuilder(); for(int i=0; i < 100; i++) { gabo.append(hoge); } return gabo.toString(); }
public static java.lang.String Test22(); Code: 0: ldc #2 // String aiueo 2: astore_0 3: new #5 // class java/lang/StringBuilder 6: dup 7: invokespecial #6 // Method java/lang/StringBuilder."":()V 10: astore_1 11: iconst_0 12: istore_2 13: iload_2 14: bipush 100 16: if_icmpge 31 19: aload_1 20: aload_0 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: pop 25: iinc 2, 1 28: goto 13 31: aload_1 32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: areturn
これは+
演算子のバイトコードよりも処理が少なくなっています
しかし、処理の少なさよりも見てほしいのはnewの位置です
for
文の処理はどこからどこまででしょうか?
28:
でgotoしているので、14:
~25:
がfor
文の処理ですね
ということは、+
演算子の時と違ってnewは1回だけです
ループ文を使って連結しまくる処理の場合は、StringBuilder
を使いましょう
Test23
※無し
Test24
- ループ使用
- String変数使用
- ループの【中】でString変数定義
- 足していく系
public static String Test24() { StringBuilder gabo = new StringBuilder(); for(int i=0; i < 100; i++) { String hoge = "aiueo"; gabo.append(hoge); } return gabo.toString(); }
public static java.lang.String Test24(); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder."":()V 7: astore_0 8: iconst_0 9: istore_1 10: iload_1 11: bipush 100 13: if_icmpge 31 16: ldc #2 // String aiueo 18: astore_2 19: aload_0 20: aload_2 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: pop 25: iinc 1, 1 28: goto 10 31: aload_0 32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: areturn
+
演算子の場合はfor
文の中でnewを何度もしていましたが、StringBuilder
を使った場合はnewは最初の1回だけです
ループで連結しまくるなら、StringBuilder
を使いましょう
Test25
※無し
Test26
- ループ使用
- 文字列使用
- ---
- 足していく系
public static String Test26() { StringBuilder gabo = new StringBuilder(); for(int i=0; i < 100; i++) { gabo.append("aiueo"); } return gabo.toString(); }
public static java.lang.String Test26(); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder."":()V 7: astore_0 8: iconst_0 9: istore_1 10: iload_1 11: bipush 100 13: if_icmpge 29 16: aload_0 17: ldc #2 // String aiueo 19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: pop 23: iinc 1, 1 26: goto 10 29: aload_0 30: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 33: areturn
+
演算子の場合はfor
文の中でnewを何度もしていましたが、StringBuilder
を使った場合はnewは最初の1回だけです
ループで連結しまくるなら、StringBuilder
を使いましょう
まとめ
まとめると、
- Javaの文字連結は
+
演算子を使い、コンパイル時の最適化に任せる方が良い StringBuilder
を使うと最適化してもらえないので、無駄な処理になることがある- とは言っても、
+
演算子ではダメな場面もあるので、その時は自分でStringBuilder
を使う
内容 | + 演算子 |
StringBuilder |
---|---|---|
Test11 1行で連結 String変数使用 |
○ 1行連結は + 演算子推奨! |
☓ |
Test12 1行で連結 文字列使用 |
○ 1行連結は + 演算子推奨! |
☓ |
Test13 複数行で連結 String変数使用 |
☓ | ○ 複数行連結なら StringBuilder もしくは1行連結に書き直すなら + 演算子が使える
|
Test14 複数行で連結 文字列使用 |
☓ | ○ 複数行連結なら StringBuilder もしくは1行連結に書き直すなら + 演算子が使える
|
Test15 1行で連結 String変数と文字列の混合使用 |
○ 1行連結は + 演算子推奨! |
☓ |
Test21 ループ使用 String変数使用 ループの【外】でString変数定義 代入系 |
○ ループ処理だけど同じ値を代入しているだけなので、何度もnewしないから + 演算子でOK(現実的にはこんな処理を書くこと自体がおかしいけどねwww) |
☓ |
Test22 ループ使用 String変数使用 ループの【外】でString変数定義 足していく系 |
☓ | ○ ループの中で毎回連結するなら StringBuilder を使いましょう
|
Test23 ループ使用 String変数使用 ループの【中】でString変数定義 代入系 |
○ Test21と同じく、固定値を代入するだけなので + 演算子でOK
|
☓ |
Test24 ループ使用 String変数使用 ループの【中】でString変数定義 足していく系 |
☓ | ○ ループの中で毎回連結するなら StringBuilder を使いましょう
|
Test25 ループ使用 文字列使用 --- 代入系 |
○ Test21と同じく、固定値を代入するだけなので + 演算子でOK
|
☓ |
Test26 ループ使用 文字列使用 --- 足していく系 |
☓ | ○ ループの中で毎回連結するなら StringBuilder を使いましょう
|
さいごに、
それは処理速度です
今回の記事はバイトコードでのnewの位置や数に着目しましたが、処理速度も重要な要素です
newでインスタンス作成→解放が処理コストかかるはずなので、newの数が多いと処理時間もかかるはずですが、
とは言え、処理速度が気になる場合には今回のようにまずバイトコードを見た上で、処理時間を計測するとよりよりソースコードになるでしょう
今回見た内容はJava8の場合です
将来的にどうなるかは分かりません
どんな場合であっても、バイトコードが見られるならば、自分の目で確認すればいいだけのことです
javapを使ってバイトコードを見ることを覚えてください
個人的にJavaに対する感想は...なんか分かりづらいんですよねぇ~
+
演算子とStringBuilder
を比較するというのが、なんか対称性がなくて、かなり変な感じがします(←この感覚分かる人には分かるでしょうけど、分からん人には分かんないですよねwww)
毎回毎回バイトコードを見るわけにはいかないですけど、ちょっとややこしいところはバイトコード見たり処理速度を見たりしましょう
あと、Javaだけじゃないですけど、プログラミングをする場合、コンパイラが最適化してくれる場合はその最適化が効くような書き方をすると良いですよ
C#はそういう思想だったはずですよね
今回はここまで!
プログラミング のレッスンに興味がある方、レッスン内容を聞いてみたい方、なんなりとお問い合わせください。
無料体験レッスンもありますのでお気軽にどうぞ!!!