StringBuiderで文字列を置換すると"+"の性能が向上しますか?
5164 ワード
回転:http://www.ticmy.com/?p=69
Javaコードの最適化を語る際に、文字列の接続操作を「+」から「StringBuider」に切り替えるというフォーラムをよく見ますが、後は簡単のために、SteringBuilderというだけです.
忙しくない答えはいくつかの例を見てください.コードは以下の通りです.
concat 1では、2つの文字の面の値(文字列の定数)を接続し、concat 1のバイトコードの0番目(0:ldc钾5;/String today is a good day)から見られます.この方法は直接常量池からロードされます.この文はコンパイル後に文字列になり、String=「String today is a good day」に相当するもので、動作中は接続操作をする必要は全くないので、文字列の面の接続にはString Buiderを使う意味がない.
concat 2では、変数参加文字列の接続です.逆コンパイルのバイトコードから分かるように、コンパイル期間はSteringBuilderのapped操作に変換されています.
最後にconcat 3を見て、forサイクルで文字列を使って接続します.最後にfor循環外で接続後の文字列を使います.バイトコードの[11,29]の間は循環体であり、循環体の中でnew StringBuiderの動作が行われていることが分かりやすく、バイトコードの代表的なコードの意味は以下の通りである.
私たちが欲しい最適化コードはこうです.
以上の3つの方法の分析により、StringBuider(StringBuffer)で文字列を置換することができますか?コンパイラは、明示的に使用できる場合以外は、有効な初期容量を与えることができます.このような意味では、個人的にはstringの「+」はStringBuiderの文法飴と考えられています.しかし、もしconcat 3のような循環中の文字列接続をするなら、我々は明示的にStringBuiderを使用する必要があります.jdk 1.4では、まだStringBuiderクラスがありません.コンパイラで生成された最適化コードはStringBufferを使用します.
String接続操作コンパイラに対して生成されたString Budler appedは、必ず単一スレッドで動作しますので、スレッドの安全問題はありません.
Javaコードの最適化を語る際に、文字列の接続操作を「+」から「StringBuider」に切り替えるというフォーラムをよく見ますが、後は簡単のために、SteringBuilderというだけです.
忙しくない答えはいくつかの例を見てください.コードは以下の通りです.
public class StringConcat {
public static void main(String... args) {
concat1();
concat2();
concat3();
}
public static void concat1() {
String s = "today is " + "a good day";
System.out.println(s);
}
public static void concat2() {
int count = 2;
String tmp = " on the desk";
String s2 = "there are " + count + " books " + tmp;
System.out.println(s2);
}
public static void concat3() {
String s3 = "";
for(int i=0; i<100; i++) {
s3 = s3 + i;
}
System.out.println(s3);
}
}
次に、これらの3つの操作文字列の方法を分析し、javap命令によって逆コンパイル.classファイル:javap-c StringContictを行い、バイトコード命令は以下の通りである(concat 1,concat 2,concat 3の3つの方法だけを摘出する).public static void concat1();
Code:
0: ldc #5; //String today is a good day
2: astore_0
3: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_0
7: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
public static void concat2();
Code:
0: iconst_2
1: istore_0
2: ldc #8; //String on the desk
4: astore_1
5: new #9; //class java/lang/StringBuilder
8: dup
9: invokespecial #10; //Method java/lang/StringBuilder."":()V
12: ldc #11; //String there are
14: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: iload_0
18: invokevirtual #13; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
21: ldc #14; //String books
23: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: aload_1
27: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
33: astore_2
34: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
37: aload_2
38: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
41: return
public static void concat3();
Code:
0: ldc #16; //String
2: astore_0
3: iconst_0
4: istore_1
5: iload_1
6: bipush 100
8: if_icmpge 36
11: new #9; //class java/lang/StringBuilder
14: dup
15: invokespecial #10; //Method java/lang/StringBuilder."":()V
18: aload_0
19: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_1
23: invokevirtual #13; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_0
30: iinc 1, 1
33: goto 5
36: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
39: aload_0
40: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
を選択して、次の3つの方法のバイトコードの意味を分析します.concat 1では、2つの文字の面の値(文字列の定数)を接続し、concat 1のバイトコードの0番目(0:ldc钾5;/String today is a good day)から見られます.この方法は直接常量池からロードされます.この文はコンパイル後に文字列になり、String=「String today is a good day」に相当するもので、動作中は接続操作をする必要は全くないので、文字列の面の接続にはString Buiderを使う意味がない.
concat 2では、変数参加文字列の接続です.逆コンパイルのバイトコードから分かるように、コンパイル期間はSteringBuilderのapped操作に変換されています.
String s2 = "there are " + count + " books " + tmp;
ステートメントはコンパイルされた後(すなわち[5,30]間の命令に相当します):String s2 = new StringBuilder().append("there are ").append(count).append(" books").append(tmp).toString();
このように、このような文字列接続コードにnew StringBuider()を明示的に使用することは、Steringの「+」オペレータがコンパイル時にnew StringBuider()・appedに変換されているため、性能の向上にはつながりません.もちろん、StringBuiderは、その初期容量としてのintパラメータを導入することができますが、これは生成されたコードではできません.プログラムだけで制御できます.最後にconcat 3を見て、forサイクルで文字列を使って接続します.最後にfor循環外で接続後の文字列を使います.バイトコードの[11,29]の間は循環体であり、循環体の中でnew StringBuiderの動作が行われていることが分かりやすく、バイトコードの代表的なコードの意味は以下の通りである.
String s3 = "";
for(int i=0; i<100; i++) {
s3 = new StringBuilder().append(s3).append(i).toString();
}
この場合、コンパイラの最適化は私達の意に及ばないです.私たちが欲しい最適化コードはこうです.
String s3 = "";
StringBuilder tmp = new StringBuilder();
tmp.append(s3);
for(int i=0; i<100; i++) {
tmp.append(i);
}
s3 = tmp.toString();
これはコンパイラにとってちょっと複雑です.手作業でできます.以上の3つの方法の分析により、StringBuider(StringBuffer)で文字列を置換することができますか?コンパイラは、明示的に使用できる場合以外は、有効な初期容量を与えることができます.このような意味では、個人的にはstringの「+」はStringBuiderの文法飴と考えられています.しかし、もしconcat 3のような循環中の文字列接続をするなら、我々は明示的にStringBuiderを使用する必要があります.jdk 1.4では、まだStringBuiderクラスがありません.コンパイラで生成された最適化コードはStringBufferを使用します.
String接続操作コンパイラに対して生成されたString Budler appedは、必ず単一スレッドで動作しますので、スレッドの安全問題はありません.