關於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性能更優。