題目
When doing string concatenation for many times in a loop, which is the fastest way in terms of executing time:
A) The concat() method of String
B) The + operator of String
C) The append() method of StringBuilder
D) The append() method of StringBuffer
解析
先看concat()的源碼
public String concat(String string) {
if (string.count > 0 && count > 0) {
char[] buffer = new char[count + string.count];
System.arraycopy(value, offset, buffer, 0, count);
System.arraycopy(string.value, string.offset, buffer, count, string.count);
return new String(0, buffer.length, buffer);
}
return count == 0 ? string : this;
}
再看“+ ”
public class ConcatString {
public static void main(String[] args) {
String a = "a";
String b = "b";
String c = a + b;
}
}
使用javap -c ConcatString.class,看看編譯器的做了什麼
public class com.nqy.mycode.test.ConcatString {
public com.nqy.mycode.test.ConcatString();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: return
}
這裡,編譯器自己做了優化,使用StringBuilder的append來連接配接,然後傳回toString().
但是否如題目所說的,在一個循環中連接配接多個String,"+"和StringBuidler效率就一樣呢?
public class ConcatString {
public static void main(String[] args) {
String r = "";
long s = System.currentTimeMillis();
for(int i=0;i<10000;i++){
r+=String.valueOf(i);
}
System.out.println(r);
System.out.println(System.currentTimeMillis() - s);
StringBuilder sb = new StringBuilder();
long s2 = System.currentTimeMillis();
for(int i=0;i<10000;i++){
sb.append(String.valueOf(i));
}
System.out.println(sb.toString());
System.out.println(System.currentTimeMillis() - s2);
}
}
執行上面的代碼,“+”使用了時間305毫秒,StringBuilder使用了3毫秒。
看編譯器是怎麼做的
Compiled from "ConcatString.java"
public class com.nqy.mycode.test.ConcatString {
public com.nqy.mycode.test.ConcatString();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String
2: astore_1
3: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
6: lstore_2
7: iconst_0
8: istore 4
10: iload 4
12: sipush 10000
15: if_icmpge 47
18: new #4 // class java/lang/StringBuilder
21: dup
22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
25: aload_1
26: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: iload 4
31: invokestatic #7 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
34: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
40: astore_1
41: iinc 4, 1
44: goto 10
47: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
50: aload_1
51: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
54: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
57: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
60: lload_2
61: lsub
62: invokevirtual #11 // Method java/io/PrintStream.println:(J)V
65: new #4 // class java/lang/StringBuilder
68: dup
69: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
72: astore 4
74: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
77: lstore 5
79: iconst_0
80: istore 7
82: iload 7
84: sipush 10000
87: if_icmpge 107
90: aload 4
92: iload 7
94: invokestatic #7 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
97: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
100: pop
101: iinc 7, 1
104: goto 82
107: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
110: aload 4
112: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
115: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
118: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
121: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
124: lload 5
126: lsub
127: invokevirtual #11 // Method java/io/PrintStream.println:(J)V
130: return
}
每循環一次都建立一個StringBuidler去append,是以開銷很大,而直接使用StringBuilder每次循環,隻需要append下就可以。
看StringBuilder和StringBuffer的差別
public synchronized StringBuffer append(char ch) {
append0(ch);
return this;
}
public StringBuilder append(char c) {
append0(c);
return this;
}
StringBuffer每次append都會檢查所否線程安全,是以其效率比StringBuilder低。
是以,答案應是C,使用StringBuilder的append()