题目
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()