在Java里拼接字符串,到底推荐用什么方式?
先说结论,+
>= StringBuilder
> String.format
。
拼接的字符串较少时,直接 +
拼接和使用 StringBuilder.append
然后 toString
有近似的性能,这是由于jdk1.8编译时,已经将 +
优化为使用 StringBuilder.append
。
甚至大多数情况下,+
拼接的性能更好点 ,因为jdk编译成字节码后,+
拼接的字符串已经编译成完成,而 StringBuilder.append
需要在运行时执行。
而当在循环中拼接的字符串,StringBuilder.append
的性能会比 +
好很多,因为 +
拼接在每次执行都需要创建 StringBuilder
对象。
不过这两者性能同时比String.format强出一截,这主要是由于String.format
需要使用正则解析输入字符串,然后再填充参数。
接下来用一个例子验证下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class StringTest {
public void testConcat() { String a = "q" + "w" + "e" + "r"; String b = new StringBuilder().append("q").append("w").append("e").append("r").toString();
String c = ""; for (int i = 0; i < 100; i++) { c = c + "q" + "w" + "e" + "r"; }
StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100; i++) { sb.append("q").append("w").append("e").append("r"); } String d = sb.toString(); }
}
|
编译:
查看字节码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| javap -c StringTest
Compiled from "StringTest.java" public class StringTest { public StringTest(); Code: 0: aload_0 1: invokespecial #1 4: return
public void testConcat(); Code: 0: ldc #2 2: astore_1 3: new #3 6: dup 7: invokespecial #4 10: ldc #5 12: invokevirtual #6 15: ldc #7 17: invokevirtual #6 20: ldc #8 22: invokevirtual #6 25: ldc #9 27: invokevirtual #6 30: invokevirtual #10 33: astore_2 34: ldc #11 36: astore_3 37: iconst_0 38: istore 4 40: iload 4 42: bipush 100 44: if_icmpge 73 47: new #3 50: dup 51: invokespecial #4 54: aload_3 55: invokevirtual #6 58: ldc #2 60: invokevirtual #6 63: invokevirtual #10 66: astore_3 67: iinc 4, 1 70: goto 40 73: new #3 76: dup 77: invokespecial #4 80: astore 4 82: iconst_0 83: istore 5 85: iload 5 87: bipush 100 89: if_icmpge 121 92: aload 4 94: ldc #5 96: invokevirtual #6 99: ldc #7 101: invokevirtual #6 104: ldc #8 106: invokevirtual #6 109: ldc #9 111: invokevirtual #6 114: pop 115: iinc 5, 1 118: goto 85 121: aload 4 123: invokevirtual #10 126: astore 5 128: return }
|
可以看到:
13行,编译中+
直接生成 "qwer"
;
17行,StringBuilder
先初始化,再依次 append
;
32行到45行,开始执行 +
操作 的循环体,每次都需要初始化 StringBuilder 对象;
85行到118行,开始执行 StringBuilder.append
操作的循环体,每次只用 append
。
从字节码来看,jdk编译后的内容和预期符合。
另外 Baeldung这篇文章 中有更详细的 Java String Performance 研究,若有兴趣可以读下。