代码测试
public static void main(String[] args) throws InterruptedException {
int testLength = 10000 ;
long l ;
String str ;
//+
l = System.currentTimeMillis();
str = "";
for (int i = 0; i < testLength; i++) {
str = str + String.valueOf(i);
}
System.out.println("+ :" + (System.currentTimeMillis() - l));
//concat
l = System.currentTimeMillis();
str = "";
for (int i = 0; i < testLength; i++) {
str.concat(String.valueOf(i));
}
System.out.println("concat :" + (System.currentTimeMillis() - l));
//StringBuilder
l = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < testLength; i++) {
sb.append(String.valueOf(i));
}
System.out.println("StringBuilder :" + (System.currentTimeMillis() - l));
}
输出结果
+ :704
concat :4
StringBuilder :2
多次试验,由上面可以大致得出以下结论,StringBuilder.append > String.concat > "+"
为什么 "+" 性能这么低?
为了探讨"+"的本质,我们打印 "+"的字节码,采用 javap -c xxx.class
命令。
12: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
15: lstore_2
16: ldc #3 // String
18: astore 4
20: iconst_0 iconst_0 是数字 0
21: istore 5
23: iload 5
25: iload_1 表示局部变量1,这里就是源码里的 i 了
26: if_icmpge 60
29: new #4 // class java/lang/StringBuilder
32: dup
33: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
36: aload 4
38: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: iload 5
43: invokestatic #7 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
46: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
52: astore 4
54: iinc 5, 1
57: goto 23
60: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
63: new #4 // class java/lang/StringBuilder
66: dup
67: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
70: ldc #10 // String + :
72: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
75: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
78: lload_2
79: lsub
80: invokevirtual #11 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
83: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
86: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
89: invokestatic #2 // Method
这里我们基本可以确定一件事情,"+"转换成了StringBuild
对象,这里但是为什么性能和StringBuild差的这么远呢?
我们分析字节码指令可知,当我们使用“+”时,循环包裹着StringBuild.
//实例
for (int i = 0; i < testLength; i++) {
str = str + String.valueOf(i);
}
//等价于
for (int i = 0; i < testLength; i++) {
StringBuilder sb = new StringBuilder();
sb.append(str);
sb.append(String.valueOf(i));
str = sb.toString();
}
**为了验证结论,我们再次分析字节码,发现二者字节码几乎一模一样。
由此,我们得到一个重要的结论
当不做循环累加时,我们使用"+"的性能和使用StringBuild.append 性能几乎一致。
StringBuild.append和String.concat性能比较
当们做对比试验时,发现StringBuild和concat的性能太相近了,很难做出试验对比,于是乎我们对比源码分析。
String.concat:
先扩容,然后在调用getChars,最后new一个类返回
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
StringBuild.append:
先扩容(与当前len比较),然后在调用getChars,最后new一个类返回
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
我们经过对比源码后可以发现StringBuild.append略快一点,因为 不用new 一个 String类返回。