“+“、String.concat、StringBuild.append 效率竞速

代码测试

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类返回。

上一篇:String、StringBuffer与StringBuilder之间区别


下一篇:Java面试题(5)String、StringBuffer、StringBuilder的区别