String和StringBuffer的區別,網上資料可以說是數不勝數,但是看到這篇文章,感覺裡面做的小例子很有代表性,所以轉一下,並自己做了一點總結。
在java中有3個類來負責字元的操作。
1.Character 是進行單個字元操作的,
2.String 對一串字元進行操作。不可變類。
3.StringBuffer 也是對一串字元進行操作,但是可變類。
String:
是對象不是原始類型.
為不可變對象,一旦被建立,就不能修改它的值.
對於已經存在的String對象的修改都是重新建立一個新的對象,然後把新的值儲存進去.
String 是final類,即不能被繼承.
StringBuffer:
是一個可變對象,當對他進行修改的時候不會像String那樣重建立立對象
它只能通過建構函式來建立,
StringBuffer sb = new StringBuffer();
note:不能通過付值符號對他進行付值.
sb = "welcome to here!";//error
對象被建立以後,在記憶體中就會分配記憶體空間,並初始儲存一個null.向StringBuffer
中付值的時候可以通過它的append方法.
sb.append("hello");
字串串連操作中StringBuffer的效率要比String高:
String str = new String("welcome to ");
str += "here";
的處理步驟實際上是通過建立一個StringBuffer,讓侯調用append(),最後
再將StringBuffer toSting();
這樣的話String的串連操作就比StringBuffer多出了一些附加操作,當然效率上要打折扣.
並且由於String 對象是不可變對象,每次操作Sting 都會重建立立新的對象來儲存新的值.
這樣原來的對象就沒用了,就要被記憶體回收.這也是要影響效能的.
看看以下代碼:
將26個英文字母重複加了5000次,
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart1 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
str += tempstr;
}
long lend1 = System.currentTimeMillis();
long time = (lend1 - lstart1);
System.out.println(time);
可惜我的電腦不是超級電腦,得到的結果每次不一定一樣一般為 46687左右。
也就是46秒。
我們再看看以下代碼
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart2 = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < times; i++) {
sb.append(tempstr);
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2);
System.out.println(time2);
得到的結果為 16 有時還是 0
所以結論很明顯,StringBuffer 的速度幾乎是String 上萬倍。當然這個資料不是很準確。因為迴圈的次數在100000次的時候,差異更大。不信你試試。
根據上面所說:
str += "here";
的處理步驟實際上是通過建立一個StringBuffer,讓侯調用append(),最後
再將StringBuffer toSting();
所以str += "here";可以等同於
StringBuffer sb = new StringBuffer(str);
sb.append("here");
str = sb.toString();
所以上面直接利用"+"來串連String的代碼可以基本等同於以下代碼
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart2 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
StringBuffer sb = new StringBuffer(str);
sb.append(tempstr);
str = sb.toString();
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2);
System.out.println(time2);
平均執行時間為46922左右,也就是46秒。
---------------------------------------------------------------------------------------------------------------------------------
StringBuffer維護了一個大小固定的字串緩衝區,當字串長度超過StringBuffer大小時會自動增加,主要使用Insert和append方法,對於運行期要進行字串的組裝操作推薦使用,
StringBuilder: jdk5以後有個和StringBuffer等價的StringBuider,區別在於StringBuffer是安全執行緒的,StringBuilder是單線程的,不提供同步,理論上效率更高。
String是系統內建類型,而且是final的,定義一個字串會產生一個執行個體和一個對該執行個體地址的引用。
如果在編譯期間定義的字串,例如 :
String a = "name";
a += "is";
a += "good";
儘管這種方法是不被推薦的,但在編譯期,編譯器會對該代碼進行最佳化,所以還是可以理解為:String a = "name is good";而如果在此時採用StringBuffer,反而會延遲到運行期才會被處理,相比之下,反而會比StringBuffer效率更高,靈活運用。
String 字串常量
StringBuffer 字串變數(安全執行緒)
StringBuilder 字串變數(非安全執行緒)
簡要的說, String 類型和 StringBuffer 類型的主要效能區別其實在於 String 是不可變的對象, 因此在每次對 String 類型進行改變的時候其實都等同於產生了一個新的 String 對象,然後將指標指向新的 String 對象,所以經常改變內容的字串最好不要用 String ,因為每次產生對象都會對系統效能產生影響,特別當記憶體中無引用對象多了以後, JVM 的 GC 就會開始工作,那速度是一定會相當慢的。
而如果是使用 StringBuffer 類則結果就不一樣了,每次結果都會對 StringBuffer 對象本身進行操作,而不是產生新的對象,再改變對象引用。所以在一般情況下我們推薦使用 StringBuffer ,特別是字串對象經常改變的情況下。而在某些特別情況下, String 對象的字串拼接其實是被 JVM 解釋成了 StringBuffer 對象的拼接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是以下的字串對象產生中, String 效率是遠要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你會很驚訝的發現,產生 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 居然速度上根本一點都不佔優勢。其實這是 JVM 的一個把戲,在 JVM 眼裡,這個
String S1 = “This is only a” + “ simple” + “test”; 其實就是:
String S1 = “This is only a simple test”; 所以當然不需要太多的時間了。但大家這裡要注意的是,如果你的字串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
這時候 JVM 會規規矩矩的按照原來的方式去做
在大部分情況下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer安全執行緒的可變字元序列。一個類似於 String 的字串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字元序列,但通過某些方法調用可以改變該序列的長度和內容。
可將字串緩衝區安全地用於多個線程。可以在必要時對這些方法進行同步,因此任意特定執行個體上的所有操作就好像是以串列順序發生的,該順序與所涉及的每個線程進行的方法調用順序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的資料。每個方法都能有效地將給定的資料轉換成字串,然後將該字串的字元追加或插入到字串緩衝區中。 append 方法始終將這些字元添加到緩衝區的末端;而 insert 方法則在指定的點添加字元。
例如,如果 z 引用一個當前內容是“start”的字串緩衝區對象,則此方法調用 z.append("le") 會使字串緩衝區包含“startle”,而 z.insert(4, "le") 將更改字串緩衝區,使之包含“starlet”。
在大部分情況下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder 一個可變的字元序列是5.0新增的。此類提供一個與 StringBuffer 相容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字串緩衝區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先採用該類,因為在大多數實現中,它比 StringBuffer 要快。兩者的方法基本相同。
通過非官方實驗測試,StringBuilder和StringBuffer的測試總結如下:
1. 為了獲得更好的效能,在構造 StirngBuffer 或 StirngBuilder 時應儘可能指定它的容量。當然,如果你操作的字串長度不超過 16 個字元就不用了。
2. 相同情況下使用 StirngBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的效能提升,但卻要冒多線程不安全的風險。而在現實的模組化編程中,負責某一模組的程式員不一定能清晰地判斷該模組是否會放入多線程的環境中運行,因此:除非你能確定你的系統的瓶頸是在 StringBuffer 上,並且確定你的模組不會運行在多線程模式下,否則還是用 StringBuffer 吧 J
3. 用好現有的類比引入新的類更重要。很多程式員在使用 StringBuffer 時是不指定其容量的(至少我見到的情況是這樣),如果這樣的習慣帶入 StringBuilder 的使用中,你將只能獲得 10 %左右的效能提升(不要忘了,你可要冒多線程的風險噢);但如果你使用指定容量的 StringBuffer ,你將馬上獲得 45% 左右的效能提升,甚至比不使用指定容量的 StirngBuilder 都快 30% 左右。