时间:2022-08-28 09:29:31 | 栏目:JAVA代码 | 点击:次
本文研讨的字符串拼接方式为以下4种:“+”号、StringBuilder、StringJoiner、String#join,对比分析及探讨最佳实践。
后面内容比较枯燥,所以先说结论:
@Slf4j
public class StringConcat {
@SneakyThrows
public static void main(String[] args) {
log.info("java虚拟机预热开始");
String[] strs = new String[6000000];
for (int i = 0; i < strs.length; i++) {
strs[i] = id();
}
loopStringJoiner(strs);
loopStringJoin(strs);
loopStringBuilder(strs);
log.info("java虚拟机预热结束");
Thread.sleep(1000);
log.info("开始测试:");
Thread.sleep(1000);
Stopwatch stopwatchLoopPlus = Stopwatch.createStarted();
// loopPlus(strs);
log.info("loop-plus: " + stopwatchLoopPlus.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchLoopStringBuilderCapacity = Stopwatch.createStarted();
loopStringBuilderCapacity(strs);
log.info("loop-stringBuilderCapacity: " + stopwatchLoopStringBuilderCapacity.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchLoopStringBuilder = Stopwatch.createStarted();
loopStringBuilder(strs);
log.info("loop-stringBuilder: " + stopwatchLoopStringBuilder.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchLoopJoin = Stopwatch.createStarted();
loopStringJoin(strs);
log.info("loop-String.join: " + stopwatchLoopJoin.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchLoopStringJoiner = Stopwatch.createStarted();
loopStringJoiner(strs);
log.info("loop-stringJoiner: " + stopwatchLoopStringJoiner.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchSimplePlus = Stopwatch.createStarted();
for (int i = 0; i < 500000; i++) {
simplePlus(id(), id(), id());
}
log.info("simple-Plus: " + stopwatchSimplePlus.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchSimpleStringBuilder = Stopwatch.createStarted();
for (int i = 0; i < 500000; i++) {
simpleStringBuilder(id(), id(), id());
}
log.info("simple-StringBuilder: " + stopwatchSimpleStringBuilder.elapsed(TimeUnit.MILLISECONDS));
Thread.sleep(1000);
Stopwatch stopwatchSimpleStringBuffer = Stopwatch.createStarted();
for (int i = 0; i < 500000; i++) {
simpleStringBuffer(id(), id(), id());
}
log.info("simple-StringBuffer: " + stopwatchSimpleStringBuffer.elapsed(TimeUnit.MILLISECONDS));
}
private static String loopPlus(String[] strs) {
String str = "";
for (String s : strs) {
str = str + "+" + s;
}
return str;
}
private static String loopStringBuilder(String[] strs) {
StringBuilder str = new StringBuilder();
for (String s : strs) {
str.append("+");
str.append(s);
}
return str.toString();
}
private static String loopStringBuilderCapacity(String[] strs) {
StringBuilder str = new StringBuilder(strs[0].length() * strs.length);
for (String s : strs) {
str.append("+");
str.append(s);
}
return str.toString();
}
private static String loopStringJoin(String[] strs) {
StringJoiner joiner = new StringJoiner("+");
for (String str : strs) {
joiner.add(str);
}
return joiner.toString();
}
private static String loopStringJoiner(String[] strs) {
return String.join("+", strs);
}
private static String simplePlus(String a, String b, String c) {
return a + "+" + b + "+" + c;
}
private static String simpleStringBuilder(String a, String b, String c) {
StringBuilder builder = new StringBuilder();
builder.append(a);
builder.append("+");
builder.append(b);
builder.append("+");
builder.append(c);
return builder.toString();
}
private static String simpleStringBuffer(String a, String b, String c) {
StringBuffer buffer = new StringBuffer();
buffer.append(a);
buffer.append("+");
buffer.append(b);
buffer.append("+");
buffer.append(c);
return buffer.toString();
}
private static String id() {
return UUID.randomUUID().toString();
}
}
结果及总结
- java虚拟机预热开始
- java虚拟机预热结束
- 开始测试:
- loop-plus: 执行超时
- loop-stringBuilderCapacity: 285
- loop-stringBuilder: 1968
- loop-String.join: 1313
- loop-stringJoiner: 1238
- simple-Plus: 812
- simple-StringBuilder: 840
- simple-StringBuffer: 857
1. StringBuilder与StringBuffer对比
在无争抢共享资源的场景下,JVM会使用偏向锁等方法优化,甚至会进行锁消除,使用Synchronized关键词与否,性能并无明显差异。
2. 字节码分析
对比上述#simplePlus和#simpleStringBuilder两个方法的字节码,可明显看到两方法执行内容基本一致,但是直接使用"+"号时处理流程更短,可见编译器进行了深度优化,使用优化后的字节码理论上会有更高的性能:
// access flags 0xA private static simplePlus(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; // parameter a // parameter b // parameter c L0 LINENUMBER 125 L0 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V ALOAD 0 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; LDC "+" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 1 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; LDC "+" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 2 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; ARETURN L1 LOCALVARIABLE a Ljava/lang/String; L0 L1 0 LOCALVARIABLE b Ljava/lang/String; L0 L1 1 LOCALVARIABLE c Ljava/lang/String; L0 L1 2 MAXSTACK = 2 MAXLOCALS = 3 // access flags 0xA private static simpleStringBuilder(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; // parameter a // parameter b // parameter c L0 LINENUMBER 129 L0 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V ASTORE 3 L1 LINENUMBER 130 L1 ALOAD 3 ALOAD 0 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L2 LINENUMBER 131 L2 ALOAD 3 LDC "+" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L3 LINENUMBER 132 L3 ALOAD 3 ALOAD 1 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L4 LINENUMBER 133 L4 ALOAD 3 LDC "+" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L5 LINENUMBER 134 L5 ALOAD 3 ALOAD 2 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L6 LINENUMBER 135 L6 ALOAD 3 INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; ARETURN L7 LOCALVARIABLE a Ljava/lang/String; L0 L7 0 LOCALVARIABLE b Ljava/lang/String; L0 L7 1 LOCALVARIABLE c Ljava/lang/String; L0 L7 2 LOCALVARIABLE builder Ljava/lang/StringBuilder; L1 L7 3 MAXSTACK = 2 MAXLOCALS = 4