字符串拼接性能:+ vs StringBuilder

3
我正在查看这个类似的帖子,其顶部回答说+StringBuilder.append()之间"没有任何性能差异",因为前者将被JVM转化为后者。
然而,根据我的基准测试,+方法始终比StringBuilder方法快约20%(我使用的是Java17,运行在Intel core i7上):
    @State(Scope.Thread)
    public static class BenchState {
        private String a = "A";
        private String b = "B";
        private String c = "C";
    }

    @Benchmark
    public void bmStringConcat(final BenchState state, final Blackhole blackhole) {
        String a = state.a;
        String b = state.b;
        String c = state.c;

        final String s = "{a:" + a + ", b:" + b + ", c: " + c + "}";
        blackhole.consume(s);
    }

    @Benchmark
    public void bmStringBuilder(final BenchState state, final Blackhole blackhole) {
        String a = state.a;
        String b = state.b;
        String c = state.c;
        StringBuilder sb = new StringBuilder();
        final String s = sb.append("{a:").append(a)
                .append(", b:").append(b)
                .append(", c:").append(c)
                .append("}")
                .toString();
        blackhole.consume(s);
    }

是因为如此处所述,"+"版本被转换为invokedynamic调用吗?
还是有其他原因?


4
既然您已经发现了2009年的答案已经过时,那么很可能invokedynamic的性能更好,因为这是引入它的目的之一。请确保不要使用Eclipse编译器来比较这些变体,因为它还不支持invokedynamic。或者,使用javap验证编译后的代码。 - Holger
1个回答

1

听从@Holger的建议,我检查了以下代码的字节码:

public class StringBM {
    public String toStringPlus(String a) {
        return "{a:" + a + ", b:" + ", c: " + "}";
    }

    public String toStringBuilder(String a) {
        StringBuilder sb = new StringBuilder(100);
        return sb.append("{a:").append(a)
                .append(", b:")
                .append(", c:")
                .append("}")
                .toString();
    }
}

对于 toStringPlus,我得到了:

  public java.lang.String toStringPlus(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1
         1: invokedynamic #7,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
         6: areturn

对于toStringBuilder

public java.lang.String toStringBuilder(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=2
         0: new           #11                 // class java/lang/StringBuilder
         3: dup
         4: bipush        100
         6: invokespecial #13                 // Method java/lang/StringBuilder."<init>":(I)V
         9: astore_2
        10: aload_2
        11: ldc           #16                 // String {a:
        13: invokevirtual #18                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        16: aload_1
        17: invokevirtual #18                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: ldc           #22                 // String , b:
        22: invokevirtual #18                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        25: ldc           #24                 // String , c:
        27: invokevirtual #18                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        30: ldc           #26                 // String }
        32: invokevirtual #18                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        35: invokevirtual #28                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        38: areturn

+版本只需调用动态函数makeConcatWithConstants
StringBuilder版本必须以“诚实”的方式完成它。
我想我们现在可以看出为什么+更快了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接