String +=:將s+=”a”生成字節(jié)碼,反編譯之后,應(yīng)該是一下代碼:
String s=(new StringBuilder()).append(s).append(“a”).toString();
也就是說使用+=的時候是先將String轉(zhuǎn)成了StringBuilder,使用其的append方法進行處理,從內(nèi)存分配上來講,又是新給了一個String,然后指向這個新的字符串,與concat相比每次不僅新New出來一塊新的內(nèi)存,并且還new了一個新的StringBuilder對象
StringBuffer:StringBuffer對象是可以修改和擴充的,直接在堆里面給分配一塊內(nèi)存,每一次使用append方法的時候,就在這個內(nèi)存塊里面在添加一個a,所以相對String要快一些,但是與StringBuilder相比又要慢一些,這是因為StringBuffer是線程安全的,它的append方法是synchronized聲明的(意思是這個方法約么全部執(zhí)行成功,要么全部不成功),是線程安全的。通俗來講就是StringBuffe的append有三個步驟,lock,臨界區(qū),unclock這三步,由于StringBuffer是多線程的,所以在for循環(huán)使用append的時候會有多個線程都分配到項目,在cpu切換內(nèi)存執(zhí)行的時候有可能線程1正在臨界區(qū)添加數(shù)據(jù) ,但是還未進入到解鎖階段,這個時候如果cpu切換到另一個線程2執(zhí)行,線程2就會發(fā)現(xiàn)線程1沒有解鎖,所以線程2就會等待,等待下一次cpu切換到線程1 繼續(xù)執(zhí)行,直到線程2發(fā)現(xiàn)線程1解鎖,那么線程2就會先加鎖,以確保自己能夠執(zhí)行完成,StringBuilder的append方法不是synchronized聲明的,所以沒有加鎖和解鎖的過程,所以也就沒有線程2的等待過程,所以會相對快一些,但是線程是不安全的,容易造成數(shù)據(jù)丟失
StringBuilder: StringBuilder的append方法是直接進入臨界區(qū),當(dāng)cpu切換到當(dāng)前程序的時候,假如當(dāng)前cpu第一次執(zhí)行這個程序,第一次進入append的for循環(huán)的時候會講數(shù)據(jù)寫進去,然后更新一下count,表示已經(jīng)使用的字符個數(shù),也可以當(dāng)作下標index來看,然后當(dāng)下一次cpu切換到線程2的時候,就會往新的index里面放數(shù)據(jù),這種情況是基于前面那個線程把數(shù)據(jù)更行完成并且更新了新的index,但是有的時候當(dāng)cpu切換到一個新的線程的時候他有可能前面那個線程append的時候,數(shù)據(jù)填寫了,Index沒有更新,這個新的線程執(zhí)行append方法的時候酒還是會在之前的Index的位置去添加數(shù)據(jù),這就造成了之前那個線程添加的數(shù)據(jù)丟失,所以StringBuilder是線程不安全的,但是因為沒有等待的過程所以相對較快,從代碼上來講StringBuilder本身有一個字符數(shù)組,每一次append就是往這個數(shù)組里面添加數(shù)據(jù),同理StringBuffer也是一樣的
concat:是final修飾的類,是不可以被繼承以及被修改的,所以每一次s+=”a”的時候事實上是在每一個常量池里面新給一個內(nèi)存塊,每一次的+=都會給一個新的內(nèi)存,一個新的地址,然后這個變量s指向新的變量,如此十萬次就有了十萬給內(nèi)存塊被new出來,所以速度相對較慢,從代碼上來講就是每一次先創(chuàng)建一個字符串?dāng)?shù)組,長度是兩個待拼接字符串長度之和,再將兩個字符串的值復(fù)制到這個字符數(shù)組中,并且使用這個數(shù)組創(chuàng)建一個新的String對象并且返回,每次都new一個新的String,源代碼如下:
public String concat(String str){
int otherLen=str.length();
if(otherLen ==0){
return this;
}
Int len=value.length;
Char buf[]=Arrays.copyOf(value,len+otherLen);
Str.getChars(buf,len);
Return new String(buf,true);
}
StriingUtils.join:與String +=類似,也是使用了StringBuilder來進行操作,但是其中還有很多其他操作,所以耗時比較長,其實StriingUtils.join更加擅長處理字符串?dāng)?shù)組的拼接
所以以上五個方法執(zhí)行從0個到十萬個a的添加,時間上的長短排序如下:
StringBuilder < StringBuffer < concat < String += < StriingUtils.join