在Java中對字符串的操作可以說是最常用的,在對字符串的操作中有三種拼接字符串的方法,下面我們來看看這三種方式有何不同,在什么時候用比較合適。
一、從耗時角度看
先來看一段代碼:
package com.codeing.snail.test;
public class StringFormat {
private static long startPointTime;
public static void main(String[] args) {
String s1 = "小明";
String s2 = "和";
String s3 = "小強";
String s4 = "一起LOL";
startPointTime = System.currentTimeMillis();
strFormat2(s1, s2);
printTimeAndClear();
strFormat1();
printTimeAndClear();
strFormat3(s1, s2, s3, s4);
printTimeAndClear();
}
private static String strFormat3(String s1, String s2, String s3, String s4) {
StringBuilder sb = new StringBuilder();
sb.append(s1);
sb.append(s2);
sb.append(s3);
sb.append(s4);
return sb.toString();
}
private static String strFormat2(String s1, String s2) {
return s1 + "和" + s2 + "一起LOL";
}
private static String strFormat1() {
return String.format("%s和%s一起LOL", new String[]{ "小明", "小強"});
}
private static void printTimeAndClear() {
System.out.println(System.currentTimeMillis() - startPointTime);
startPointTime = System.currentTimeMillis();
}
}
輸出結果:

從上面的結果我們可以看出,在拼接字符串的時候使用format方式比較耗時,但是這樣就得出結論還太早,下面我們來循環1000次看一下。
package com.codeing.snail.test;
public class StringFormat {
private static long startPointTime;
public static void main(String[] args) {
String s1 = "小明";
String s2 = "和";
String s3 = "小強";
String s4 = "一起LOL";
startPointTime = System.currentTimeMillis();
strFormat2(s1, s2);
printTimeAndClear();
//strFormat1();
//printTimeAndClear();
strFormat3(s1, s2, s3, s4);
printTimeAndClear();
}
private static String strFormat3(String s1, String s2, String s3, String s4) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 1000; i++){
sb.append(String.valueOf(i));
}
return sb.toString();
}
private static String strFormat2(String s1, String s2) {
String str = "begin";
for(int i = 0; i < 1000; i++){
str = str + String.valueOf(i);
}
return str;
}
private static String strFormat1() {
String str = "begin";
String[] strArr = new String[1000];
for(int i = 0; i < strArr.length; i++){
strArr[i] = String.valueOf(i);
}
//我勒個去,這不會讓我寫1000個他匹配符吧。。。顯然這種方式不適合大量的拼接。
//return String.format("%s%s%s......", strArr);
return str;
}
private static void printTimeAndClear() {
System.out.println(System.currentTimeMillis() - startPointTime);
startPointTime = System.currentTimeMillis();
}
}
輸出結果:

上面是用“+”號拼接字符串的結果,下面是使用StringBuilder的結果,很顯然在拼接很頻繁的情況下使用StringBuilder比使用“+”號拼接好,這種情況下使用format可以不予考慮。
綜合比較:StringBuilder勝出,如果拼接的字符串比較多則可以優先考慮耗時。
二、從消耗內存角度看
這個角度我們就要從源代碼尋找答案,先來看看StringBuilder的源碼:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}其中value是一個char[]類型,我們可以大概了解到實現字符串拼接的原理是字符數組的拷貝操作實現的。其實兩個字符串相加的本質也是StringBuilder的方式相加的,但是會創建多余的字符串對象。
下面我們來看一下String.format的源碼:
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}java.util.Formatter這個類可能大家比較熟悉,這個是格式化文本輸出的類,比如日期、金額等,我們最常見的System.out.println()也是通過調用此類的format方法實現的。
Formatter的構造方法如下:
public Formatter() {
this(Locale.getDefault(Locale.Category.FORMAT), new StringBuilder());
}
/* Private constructors */
private Formatter(Locale l, Appendable a) {
this.a = a;
this.l = l;
this.zero = getZero(l);
}
可以看到創建了一個StringBuilder作為了Formatter的全局變量。
public Formatter format(Locale l, String format, Object ... args) {
ensureOpen();
// index of last argument referenced
int last = -1;
// last ordinary index
int lasto = -1;
FormatString[] fsa = parse(format);
for (int i = 0; i < fsa.length; i++) {
FormatString fs = fsa[i];
int index = fs.index();
try {
switch (index) {
case -2: // fixed string, "%n", or "%%"
fs.print(null, l);
break;
case -1: // relative index
if (last < 0 || (args != null && last > args.length - 1))
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[last]), l);
break;
case 0: // ordinary index
lasto++;
last = lasto;
if (args != null && lasto > args.length - 1)
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[lasto]), l);
break;
default: // explicit index
last = index - 1;
if (args != null && last > args.length - 1)
throw new MissingFormatArgumentException(fs.toString());
fs.print((args == null ? null : args[last]), l);
break;
}
} catch (IOException x) {
lastException = x;
}
}
return this;
}在format方法中我們看到一個FormatString的print方法如下:
public void print(Object arg, Locale l)
throws IOException { a.append(s); }a就是我們上面創建的StringBuilder,這下我們就明白了,原來String.format也是通過StringBuilder來時實現的。
通過上面分析我們可以得出這樣的結論:StringBuilder是其他兩種方式的基礎實現,所以還是StringBuilder比較占優勢。
綜合比較:StringBuilder勝出,如果拼接的字符串比較多則可以優先考內存時。結合耗時我們可以得出這樣的結論,在使用大量字符串拼接的時候優先使用StringBuilder.
三、從實際使用角度考慮
上面分析了兩點,使用String.format和"+“號拼接字符串貌似毫無優勢,那么為什么還要比較?其實對於有些場合(比如兩個字符串拼接)我們為了程序的可讀性、簡潔等因素可以使用這兩種方式。或者遇到要使用一些特殊格式的字符串(比如日期顯示)或者進度刻度之間轉換時我們就可以使用String.format來拼接。
