一、字符串拼接方式:+和concat和StringBuilder
源碼:
public static void main(String[] args) {
String str = "";
System.out.println("執(zhí)行加號拼接:");
long starttime = System.currentTimeMillis();
System.out.println("執(zhí)行拼接之前的時間:"+starttime);
for (int i = 0; i < 50000; i++) {
str += "a";
}
long latertime = System.currentTimeMillis();
System.out.println("執(zhí)行拼接之后的時間:"+latertime);
System.out.println("用加號拼接所花費的時間:"+(latertime - starttime));
System.out.println("result== " + str);
System.out.println("--------------------------------");
String str1 = "";
System.out.println("執(zhí)行concat拼接:");
long starttime1 = System.currentTimeMillis();
System.out.println("執(zhí)行拼接之前的時間:"+starttime1);
for (int i = 0; i < 50000; i++) {
str1 = str1.concat("b");
}
long latertime1 = System.currentTimeMillis();
System.out.println("執(zhí)行拼接之后的時間:"+latertime1);
System.out.println("用加號拼接所花費的時間:"+(latertime1 - starttime1));
System.out.println("result=="+str1);
System.out.println("--------------------------------");
StringBuilder stringBuilder = new StringBuilder();
System.out.println("執(zhí)行StringBuilder拼接:");
long starttime2 = System.currentTimeMillis();
System.out.println("執(zhí)行拼接之前的時間:"+starttime2);
for (int i = 0; i < 50000; i++) {
stringBuilder.append("c");
}
long latertime2 = System.currentTimeMillis();
System.out.println("執(zhí)行拼接之后的時間:"+latertime2);
System.out.println("用加號拼接所花費的時間:"+(latertime2 - starttime2));
System.out.println("result=="+stringBuilder.toString());
}
執(zhí)行結果圖:

由此可見,StringBuilder最快,concat次之,加號拼接最慢。
二、為什么會出現(xiàn)這樣的情況
2.1 "+"方法實現(xiàn)原理
內部實現(xiàn)采用StringBuilder的append方法進行追加,最終以toString方法轉換成String字符串,比如:
String wechat = "Hollis";
String introduce = "每日更新Java相關技術文章";
String hollis = wechat + "," + introduce;
反編譯后的結果如下,反編譯工具為jad。
String wechat = "Hollis";
String introduce = "\u6BCF\u65E5\u66F4\u65B0Java\u76F8\u5173\u6280\u672F\u6587\u7AE0";
String hollis = (new StringBuilder()).append(wechat).append(",").append(introduce).toString();
通過查看反編譯后的代碼,我們可以發(fā)現(xiàn),在拼接字符串常量中,是將String轉成了StringBuilder后,使用其append方法進行處理,也就是說,,在java中使用“+”對字符串拼接,采用的原理是使用StringBuilder。
但是它與純粹使用StringBuilder的append方法是不同的:
1、每次執(zhí)行循環(huán)添加字符時,它都會創(chuàng)建一個StringBuilder對象,
2、每次執(zhí)行完后都會調用toString方法將其轉換為字符串
所以耗費了更多時間。
2.2 "concat"方法實現(xiàn)原理
concat源代碼
public String concat(String str) {
int otherLen = str.length();//獲取添加的字符串長度
if (otherLen == 0) {//如果字符串長度為0,則返回字符串長度本身
return this;
}
int len = value.length;//獲取原字符串的字符數(shù)組的長度
char buf[] = Arrays.copyOf(value, len + otherLen);//將原字符串的字符數(shù)組放到buff數(shù)組中
str.getChars(buf, len);//追加的字符串轉化成字符數(shù)組,添加到buf中
return new String(buf, true);//產(chǎn)生一個新的字符串返回
}
整體的實現(xiàn)過程就是完成對數(shù)組的拷貝,雖然在內存中處理是原子性操作,速度非???,但是,最后的return語句創(chuàng)建一個新的String對象,也就是每次concat操作都會創(chuàng)建一個新的String對象,這也是限制concat方法速度的原因。
2.3 "append"方法實現(xiàn)原理
代碼實現(xiàn)
public AbstractStringBuilder append(String str) {
if (str == null)//如果是null值,則把null作為字符串處理
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);//追加后的字符數(shù)組長度是否超過當前值
str.getChars(0, len, value, count);//將字符串復制到目標數(shù)組
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l'; //將null添加到char[] value數(shù)組中
count = c; //將count的值初始化
return this;//返回結果
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));//加長數(shù)組空間,并作數(shù)組拷貝
}
}
append方法都在做字符數(shù)組的處理,加長,拷貝等,這些都是基本的數(shù)據(jù)處理,整個方法內并沒有生成對象,只是最后執(zhí)行了toString方法返回了一個對象而已
三、題外
String str = "My name is ";
str = str + "JTZen9";
相當于str = new StringBuilder(str).append("JTZen9").toString();
也就是說,該str = str + "JTZen9",語句執(zhí)行完后,總共有三個對象
String str = "My name is " + "JTZen9";
JVM會直接把str作為一個對象,即“My name is JTZen9”
四、使用場景
1、大多數(shù)情況,我們使用“+”,符合編碼習慣和我們的閱讀
2、當在頻繁進行字符串的運算(如拼接、替換、刪除等),或者在系統(tǒng)性能臨界的時候,我們可以考慮使用concat或append方法