讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議56:自由選擇字符串拼接方法 >

建議56:自由選擇字符串拼接方法

對一個字符串進行拼接有三種方法:加號、concat方法及StringBuilder(或StringBuffer,由於StringBuffer的方法與StringBuilder相同,文中不再贅述)的append方法,其中加號是最常用的,其他兩種方式偶爾會出現在一些開源項目中,那這三者之間有什麼區別嗎?我們來看下面的例子:


//加號拼接

str+="c";

//concat方法連接

str=str.concat("c");


上面是兩種不同的字符串拼接方式,循環5萬次後再檢查其執行的時間,加號方式的執行時間是1438毫秒,而concat方法的執行時間是703毫秒,時間相差1倍,如果使用StringBuilder方式,執行時間會更少,其代碼如下:


public static void doWithStringBuffer(){

StringBuilder sb=new StringBuilder("a");

for(int i=0;i<50000;i++){

sb.append("c");

}

String str=sb.toString();

}


StringBuffer的append方法的執行時間是0毫秒,說明時間非常非常短暫(毫秒不足以計時,讀者可以使用納秒進行計算)。這個實驗也說明在字符串拼接方式中,append方法最快,concat方法次之,加號最慢,這是為何呢?

(1)「+」方法拼接字符串

雖然編譯器對字符串的加號做了優化,它會使用StringBuilder的append方法進行追加,按道理來說,其執行時間也應該是0毫秒,不過它最終是通過toString方法轉換成String字符串的,例子中「+」拼接的代碼與如下代碼相同:


str=new StringBuilder(str).append("c").toString();


注意看,它與純粹使用StringBuilder的append方法是不同的:一是每次循環都會創建一個StringBuilder對象,二是每次執行完畢都要調用toString方法將其轉換為字符串——它的執行時間就是耗費在這裡了!

(2)concat方法拼接字符串

我們從源碼上看一下concat方法的實現,代碼如下:


public String concat(String str){

int otherLen=str.length();

//如果追加的字符串長度為0,則返回字符串本身

if(otherLen==0){

return this;

}

//字符數組,容納的是新字符串的字符

char buf=new char[count+otherLen];

//取出原始字符串放到buf數組中

getChars(0,count, buf,0);

//追加的字符串轉化成字符數組,添加到buf中

str.getChars(0,otherLen, buf, count);

//複製字符數組,產生一個新的字符串

return new String(0,count+otherLen, buf);

}


其整體看上去就是一個數組拷貝,雖然在內存中的處理都是原子性操作,速度非常快,不過,注意看最後的return語句,每次的concat操作都會新創建一個String對象,這就是concat速度慢下來的真正原因,它創建了5萬個String對像呀!

(3)append方法拼接字符串

StringBuilder的append方法直接由父類AbstractStringBuilder實現,其代碼如下:


public AbstractStringBuilder append(String str){

//如果是null值,則把null作為字符串處理

if(str==null)str="null";

int len=str.length();

//字符串長度為0,則返回自身

if(len==0)return this;

int newCount=count+len;

//追加後的字符數組長度是否超過當前值

if(newCount>value.length)

expandCapacity(newCount);//加長,並做數組拷貝

//字符串複製到目標數組

str.getChars(0,len, value, count);

count=newCount;

return this;

}


看到沒,整個append方法都在做字符數組處理,加長,然後數組拷貝,這些都是基本的數據處理,沒有新建任何對象,所以速度也就最快了!注意:例子中是在最後通過StringBuffer的toString返回了一個字符串,也就是說在5萬次循環結束後才生成了一個String對象。

三者的實現方法不同,性能也就不同,但並不表示我們一定要使用StringBuilder,這是因為「+」非常符合我們的編碼習慣,適合人類閱讀,兩個字符串拼接,就用加號連一下,這很正常,也很友好,在大多數情況下我們都可以使用加號操作,只有在系統性能臨界(如在性能「增之一分則太長」的情況下)的時候才可以考慮使用concat或append方法。而且,很多時候系統80%的性能是消耗在20%的代碼上的,我們的精力應該更多的投入到算法和結構上。

注意 適當的場景使用適當的字符串拼接方式。