1. 三者區別
String 字符串常量
StringBuffer 字符串變量(線程安全)
StringBuilder 字符串變量(非線程安全)
1.1 String
簡要的說, String 類型和 StringBuffer 類型的主要性能區別其實在於 String 是不可變的對象, 因此在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,然後將指針指向新的 String 對象,所以經常改變內容的字符串最好不要用 String ,因爲每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了以後, JVM 的 GC 就會開始工作,速度是一定會相當慢的。
而如果是使用 StringBuffer 類則結果就不一樣了,每次結果都會對 StringBuffer 對象本身進行操作,而不是生成新的對象,再改變對象引用。所以在一般情況下我們推薦使用 StringBuffer ,特別是字符串對象經常改變的情況下。而在某些特別情況下,String 對象的字符串拼接其實是被 JVM 解釋成了 StringBuffer 對象的拼接,所以這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是以下的字符串對象生成中, String 效率是遠要比 StringBuffer 快的:
你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 居然速度上根本一點都不佔優勢。
其實這是 JVM 的一個把戲,在 JVM 眼裏,這個
其實就是: 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
1.2 StringBuffer
Java.lang.StringBuffer
線程安全的可變字符序列。一個類似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。
可將字符串緩衝區安全地用於多個線程。可以在必要時對這些方法進行同步,因此任意特定實例上的所有操作就好像是以串行順序發生的,該順序與所涉及的每個線程進行的方法調用順序一致。
StringBuffer 上的主要操作是 append
和insert
方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串緩衝區中。
例如:
如果 str 引用一個當前內容是“start”的字符串緩衝區對象,則此方法調用 str.append("LE")
會使字符串緩衝區含“startLE”
而 str.insert(4, “LE”) 將更改字符串緩衝區,使之包含“starLEt”
在大部分情況下 StringBuilder > StringBuffer
1.3 StringBuilder
java.lang.StringBuilder
java.lang.StringBuilder一個可變的字符序列是JDK1.5新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種情況很普遍)。如果可能,建議優先採用該類,因爲在大多數實現中,它比StringBuffer 要快。兩者的方法基本相同。
從線程安全的角度去看
StringBuffer是線程安全的,而StringBuilder是線程不安全的
【StringBuffer線程安全,StringBuilder線程不安全】
2. 性能測試
這三個類型到底有什麼區別呢?怎麼選擇它們的應用場景呢?
首先,從性能、速度方面來說:
StringBuilder > StringBuffer > String
我們來做一個測試,我們分別使用String和StringBuilder創建變量,然後分別對它們進行加字符串操作,由於時間太短,我們把這個過程使用for循環重複10W遍以放大差距:
public class TestBuffer {
public static void main(String[] args) {
//獲取開始時間
Long start1 = System.currentTimeMillis();
//String Test
for (int i = 0;i < 100000;i++) {
String str = "a";
str += "b";
}
//獲取結束時間
Long end1 = System.currentTimeMillis();
//打印出花費的時間
System.out.println("String花費時間:"+ (end1-start1));
//StringBuilder Test
Long start2 = System.currentTimeMillis();
for (int i = 0; i < 100000;i++) {
StringBuilder str2 = new StringBuilder("a");
str2.append("b");
}
Long end2 = System.currentTimeMillis();
System.out.println("StringBuilder花費時間:"+(end2-start2));
//重複10萬次進行StringBuffer變量加操作
Long start3 = System.currentTimeMillis();
for (int i=0;i < 100000;i++)
{
StringBuffer str2 = new StringBuffer("a");
str2.append("b");
}
Long end3 = System.currentTimeMillis();
System.out.println("StringBuffer花費時間:"+(end3-start3));
}
}
是什麼造成了這種差距呢?
在上面的程序中:
這兩句話看似是對同一個String類型的str
對象進行了加操作,但是實際上可不是同一個對象。
事實上,我們先聲明瞭一個String類型的對象,值是"a",把str這個引用指向了這個對象。然後,當我們把這個對象進行**+=**操作的時候,實際上是又創建了一個新的String對象,這個對象的值是"a"+“b"也就是"ab”,然後改變str
的引用讓它指向了這個新的對象,原來的對象失去了引用,就被jvm垃圾回收了。
如果將這兩句代碼
改爲
讓我們看一下執行效果:
哪怕是進行10萬次操作,String所花費的時間也是極少的,這是爲什麼呢?
當我們執行:String str = “a”+“b”;這句話的時候,String會自動把這個對象的值看成"ab",然後在方法區中如果找到了值同樣爲"ab"的,就會直接讓str引用指向它
而 StringBuffer 和 StringBuilder 可不是這樣,這兩兄弟是直接改變自己本身對象的值。
那麼,當我們進行了10萬次操作的時候,快慢差距自然就體現出來了。
3. StringBuffer,StringBuilder的用法
toString()
將StringBuffer,StringBuilder對象轉換爲String字符串,常用在需要輸出的時候,因爲StringBuffer和StringBuilder的對象不能直接輸出,需要調用toString()
append()
用於在字符串的後面追加字符串. 當StringBuffer,StringBuilder中沒有字符串的時候也可以append(),可以用來初始化
insert()
在指定索引位置之前插入字符串
其他方法類比String類中常用的方法
charaAt()
返回指定索引位置的字符,索引從0開始
deleteCharAt()
刪除指定索引位置的字符
delete()
刪除從開始索引到結束索引的字符串
reverse()
反轉字符串
總結
- String 適用於少量的字符串操作的情況
- StringBuilder 適用於單線程下在字符緩衝區進行大量操作的情況
- StringBuffer 適用多線程下在字符緩衝區進行大量操作的情況
【文章轉載彙總,原文鏈接】
String,StringBuffer與StringBuilder的區別??