在平常的開發(fā)都說對于字符串的拼湊時,要盡量使用StringBuilder來操作零渐,特別是對于長字符串的拼湊窒舟。但是卻很少知道為什么要這樣做,在直接使用字符串進行拼湊時诵盼,編譯器到底是怎么將"+"完成字符串拼湊的惠豺,它的弊端又是上面,而使用StringBuilder內部又是如何優(yōu)化的呢风宁?
現(xiàn)在我們來扒一扒其中的緣由洁墙,知其然而知其所以然。
首先我們來看下面這個例子:
public class StringDemo2{
public static void main(String[] args) {
String str = "";
for (int i = 0; i < 10; i++) {
str+=i;
}
System.out.println(str);
}
}
在拼湊str3字符串時戒财,編譯器在編譯該.java文件時热监,幫我們添加了上面東西呢?我們通過javap指令來查看編譯后.class指令文件
在編譯期饮寞,編譯器會幫我們生成一個StringBuilder對象狼纬,同時將使用+拼湊字符的過程,轉換成調用StringBuilder的append( )方法進行拼湊骂际。
這是有人會說內部的實現(xiàn)跟我們直接用StringBuilder對象進行拼湊是一樣的啊疗琉,都是使用StringBuilder,都是使用append方法。其實細看還是有很大差別的歉铝,編譯器并不會很智能盈简,它只能將有+號操作的做出轉換成StringBilder對象的append方法,作完這個+操作后,又調用了StringBuilder的toString()方法太示,該方法的內部是直接new String(.. )對象進行返回字符串的柠贤。接下來又繼續(xù)重復該動作,進行拼湊字符串
這樣不斷的new StringBuilder() --> append()--->toString(), new StringBuilder() --> append()--->toString() ...的操作类缤,將會創(chuàng)建大量的對象臼勉,而真正需要使用的字符串只是最后拼湊好的字符串,這樣則造成的大量內存的浪費餐弱。
對于少量字符串的拼湊還算能接觸宴霸,但對于大量且長文本的字符串則對于系統(tǒng)的開銷就很大了。像這種不必要的內存浪費膏蚓,我們當然要杜絕發(fā)生了瓢谢。
而直接使用StringBuilder,拼湊字符串驮瞧,其內部又是如何做的呢氓扛?
我們先將上面的例子改成StringBuilder來實現(xiàn)
public class StringDemo2{
public static void main(String[] args) {
String str = "";
StringBuilder sb = new StringBuilder(str);
for (int i = 0; i < 10; i++) {
sb.append(i);
}
System.out.println(sb.toString());
}
}
同樣適用javap查看文件指令:
從該字節(jié)碼指令可以看出,在拼湊字符串時论笔,只生成了一個StringBuilder對象用于拼湊采郎,拼湊完后只生成了一個Sting對象千所,這樣大大節(jié)省了內存開銷,提高了效率蒜埋。
由于String字符串是不可變的真慢,每次的該包都會生成新的字符串,而StringBuilder內部是如何實現(xiàn)在內部append( )方法時提供復用性的呢理茎?
我們來看到StringBuilder源碼,首先StringBuilder繼承至一個AbstractStringBuilder的抽象類管嬉。
在該AbstractStringBuilder抽象類中皂林,在該類中有兩個重要的成員變量:
其中char[ ] value就是用來實現(xiàn)字符串緩沖池的核心,count數(shù)是用來記錄StringBuilder內部中真實的字符長度的蚯撩。細心的小伙伴可能要問了础倍,干嘛不直接使用char[] 數(shù)組的長度表示字符的長度呢?繼續(xù)往下看就知道了胎挎。
在初始化一個StringBuilder對象是沟启,如果使用個都是默認構造器進行初始化,在StringBuilder的默認構造器會調用父類抽象類的構造器犹菇,并傳入默認16的數(shù)值德迹,該數(shù)值用于初始化容緩沖池的大小,也就是初始化char[ ] 數(shù)組的長度揭芍,所有我們不能直接使用該長度來表示實際字符長度的胳搞。
調用append(String str)方法時,會調用其父類的append( )方法称杨,所有要查看append( )方法的內部實現(xiàn)肌毅,還是得去AbstractStringBuilder類中
進入AbstractStringBuilder 類的append方法
我們發(fā)現(xiàn),其內部的實現(xiàn)還是比較簡單的姑原⌒總的來說內部對append字符串的操作,在建立在char[ ]數(shù)組上的锭汛,當有新的字符串append進來時笨奠,會將該字符串轉會成對應的char[ ]字符數(shù)組 ,然后調用System.copyArray( ... )方法將其拷貝至StringBuilder內部char[ ] value數(shù)組中唤殴。這樣在拼湊字符串時艰躺,就不會像直接拼湊字符串那樣不斷創(chuàng)建新的String對象了。
在方法中還有一個內部字符容器擴容操作眨八,ensureCapacityInternal(count + len);順便我們來看出下該方法的操作腺兴,
minimumCapacity參數(shù)就是那個 count(原有的字符數(shù)) + len(新加入的字符數(shù))得到的值,如果該值大于了char[] valueStringBuilder內部的緩沖數(shù)組的長度廉侧,則說明原有的容器已經不夠裝了页响,需要對其進行擴容操作篓足。
將value原有的值都拷貝至一個新的char[ ] 數(shù)組中,該數(shù)組的長度是原來的2倍+2闰蚕。從這也可以看出栈拖,在使用StringBuilder拼湊廠字符串時,應該預估一個StringBuilder內部char[ ]容器的初始化長短值没陡,這樣就可以盡量的避免頻繁擴容帶來的性能損耗涩哟。