关于Java字符串的拼接,你了解多少?
前景
昨晚开了一个review会,就在循环内部创建String对象的问题展开了激烈的讨论。今早小伙伴们意犹未尽,就字符串的拼接又一次在群里炸开了锅。作为一个有几年工作经验的我来说,在这次的讨论中受益匪浅。
代码中常见的字符串拼接方式
用“+”拼接字符串
String ab = "a" + "b";
输出 : ab
String.format()
String ab = String.format("%s%s","a","b");
输出 : ab
StringBuilder和StringBuffer
StringBuilder sb = new StringBuilder()
sb.append(“a”);
sb.append(“b”);
String ab = sb.toString();
输出 : ab
性能测试
网上对它们的性能普遍认为“+号”性能较差。为了看出效果,时间单位为微秒,纳秒有点太小了。
String a1 = "a";
String b1 = "b";
Thread.sleep(1000);
long start1 = System.nanoTime();
String ab1 = a1 + b1;
long end1 = System.nanoTime();
System.out.println("+号拼接 : " + ((end1 - start1) / 1000) + "ws");
String a2 = "a";
String b2 = "b";
Thread.sleep(1000);
long start2 = System.nanoTime();
String ab2 = String.format("%s%s", a2, b2);
long end2 = System.nanoTime();
System.out.println("String.format() : " + ((end2 - start2) / 1000)+ "ws");
String a3 = "a";
String b3 = "b";
Thread.sleep(1000);
long start3 = System.nanoTime();
StringBuilder sb = new StringBuilder();
sb.append(a3);
sb.append(b3);
String ab3 = sb.toString();
long end3 = System.nanoTime();
System.out.println("StringBuilder.append() : "+ ((end3 - start3) / 1000)+ "ws");
输出 :
+号拼接 : 127ws
String.format() : 46584ws
StringBuilder.append() : 62ws
你可以再试试5以内的字符串拼接。结果差不多。
结果
在2-5个数左右的字符串拼接中“+”号拼接和StringBuilder的耗时相差无几,而String.format()耗时较多
分析
为了进一步了解String,查看到了JDK1.6的源码,String.class开头注释写到
- The Java language provides special support for the string
- concatenation operator ( + ), and for conversion of
- other objects to strings. String concatenation is implemented
- through the <code>StringBuilder</code>(or <code>StringBuffer</code>)
- class and its <code>append</code> method.
- String conversions are implemented through the method
- <code>toString</code>, defined by <code>Object</code> and
- inherited by all classes in Java. For additional information on
- string concatenation and conversion, see Gosling, Joy, and Steele,
- <i>The Java Language Specification</i>.
这段话的大致意思就是说
java语言提供了字符串串联运算符,和其他对象转换为字符串,字符串连接是底层通过StringBuilder或者StringBuffer来实现的。
这下就明白了为什么“+”号拼接和StringBuilder拼接的耗时相差无几了,但为什么String.format()的耗时相差甚远呢?
public static String format(String format, Object ... args) {
return new Formatter().format(format, args).toString();
}
public Formatter() {
init(new StringBuilder(), Locale.getDefault());
}
在上述代码中不难看出底层也是用StringBuilder实现的,这就不能理解了,同样是StringBuilder,为什么相差那么大呢?咱们接着往下看。
private FormatString[] parse(String s) {
ArrayList al = new ArrayList();
Matcher m = fsPattern.matcher(s);
...
}
上述代码是在new Formatter().format()方法中找到的,原来内部有正则匹配操作。众所周知,正则匹配是很消费性能的。这下可以解开心中的迷惑了。
结论
- 就可读性和简洁性而言,数量不多的拼接,“+”号拼接是个很不错的字符串拼接方式。
- String.format()还没找到适合用他的场景。(也许是需要正则匹配,有的话请告诉我)
- 循环中StringBuilder和StringBuffer性能更优。