字符串连接性能:+ vs StringBuilder
String concatenation performance: + vs StringBuilder
我正在检查 this similar post ,其中的最佳答案说 +
和 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
调用”here.
还是有更多的原因?
听从@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
版本必须以 'honest' 方式进行。
我想我们可以明白为什么 +
现在更快了。
我正在检查 this similar post ,其中的最佳答案说 +
和 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
调用”here.
还是有更多的原因?
听从@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
版本必须以 'honest' 方式进行。
我想我们可以明白为什么 +
现在更快了。