Java之String、StringBuffer和StringBuilder的區別
近期看到StringBuffer,便搜尋整理了一些關於String、StringBuffer和StringBuilder的東西,僅供分享參考。
1、String
String:字串常量,字串長度不可變的。Java中String是immutable(不可變)的。先看原始碼:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; ... ... }
String類使用了final修飾,那麼使用了final修飾就規定了String是不能被繼承的,換句話說,有一個String s=”abc”,s引用我們可以很明確的知道s就是指向的String型別的物件,而不是String型別的子類,這樣從jvm的角度考慮,是提升了一定的效率,而且可以看到char value[]也是final,這說明了裡面的char陣列也是不能改變的,只能進行一次賦值,而且我們都知道陣列一旦長度固定,就沒有辦法擴容,所以這就是為什麼String物件裡面的值不能改變的原因,因為String物件一旦建立,char陣列的長度就剛好夠存字串的長度,以後就沒有辦法擴充了。
String類的包含如下定義:
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
用於存放字元的陣列被宣告為final的,因此只能賦值一次,不可再更改。
2、StringBuffer
StringBuffer:字串變數(Synchronized,即執行緒安全)。如果要頻繁對字串內容進行修改,出於效率考慮最好使用StringBuffer,如果想轉成String型別,可以呼叫StringBuffer的toString()方法。
Java.lang.StringBuffer執行緒安全的可變字元序列。在任意時間點上它都包含某種特定的字元序列,但通過某些方法呼叫可以改變該序列的長度和內容。可將字串緩衝區安全地用於多個執行緒。
StringBuffer 上的主要操作是 append 和 insert 方法,可過載這些方法,以接受任意型別的資料。每個方法都能有效地將給定的資料轉換成字串,然後將該字串的字元追加或插入到字串緩衝區中。append 方法始終將這些字元新增到緩衝區的末端;而 insert 方法則在指定的點新增字元。例如,如果 z 引用一個當前內容是“start”的字串緩衝區物件,則此方法呼叫 z.append("le") 會使字串緩衝區包含“startle”,而 z.insert(4, "le") 將更改字串緩衝區,使之包含“starlet”。
3、StringBuilder
StringBuilder:字串變數(非執行緒安全)。在內部,StringBuilder物件被當作是一個包含字元序列的變長陣列。
java.lang.StringBuilder是一個可變的字元序列,是JDK5.0新增的。此類提供一個與 StringBuffer 相容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字串緩衝區被單個執行緒使用的時候(這種情況很普遍)。
其構造方法如下:
構造方法 | 描述 |
StringBuilder() | 建立一個容量為16的StringBuilder物件(16個空元素) |
StringBuilder(CharSequence cs) | 建立一個包含cs的StringBuilder物件,末尾附加16個空元素 |
StringBuilder(int initCapacity) | 建立一個容量為initCapacity的StringBuilder物件 |
StringBuilder(String s) | 建立一個包含s的StringBuilder物件,末尾附加16個空元素 |
在大部分情況下,StringBuilder > StringBuffer。這主要是由於前者不需要考慮執行緒安全。
4、三者的區別
(1)在執行速度上,StringBuilder > StringBuffer > String
(2)String <(StringBuffer,StringBuilder)的原因
String:字串常量
StringBuffer:字串變數
StringBuilder:字串變數
從上面的名字可以看到,String是“字串常量”,也就是不可改變的物件。對於這句話的理解你可能會產生這樣一個疑問 ,比如這段程式碼:
String s = "abcd";
s = s+1;
System.out.print(s);// result : abcd1
我們明明就是改變了String型的變數s的,為什麼說是沒有改變呢?
其實這是一種欺騙,JVM是這樣解析這段程式碼的:
首先建立物件s,賦予一個abcd,然後再建立一個新的物件s用來執行第二行程式碼,也就是說我們之前物件s並沒有變化,所以我們說String型別是不可改變的物件了,由於這種機制,每當用String操作字串時,實際上是在不斷的建立新的物件,而原來的物件就會變為垃圾被GC(Garbage Collection,垃圾回收器)回收掉,可想而知這樣執行效率會有多底。
而StringBuffer與StringBuilder就不一樣了,他們是字串變數,是可改變的物件,每當我們用它們對字串做操作時,實際上是在一個物件上操作的,這樣就不會像String一樣建立一些而外的物件進行操作了,當然速度就快了。
(3)一個特殊的例子
在某些特別情況下, String 物件的字串拼接其實是被 Java Compiler 編譯成了 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慢。其實在Java Compiler裡,自動做了如下轉換:
Java Compiler直接把上述第一條語句編譯為:
String s1 = “This is only a simple test”;
所以速度很快。但要注意的是,如果拼接的字串來自另外的String物件的話,Java Compiler就不會自動轉換了,速度也就沒那麼快了,例如:
String s2 = “This is only a”;
String s3 = “ simple”;
String s4 = “ test”;
String s1 = s2 + s3 + s4;
這時候,Java Compiler會規規矩矩的按照原來的方式去做,String的concatenation(連線,即+)操作利用了StringBuilder(或StringBuffer)的append方法實現,此時,對於上述情況,若s2,s3,s4採用String定義,拼接時需要額外建立一個StringBuffer(或StringBuilder),之後將StringBuffer轉換為String;若採用StringBuffer(或StringBuilder),則不需額外建立StringBuffer。
(4)StringBuilder與 StringBuffer
StringBuilder:執行緒非安全的
StringBuffer:執行緒安全的
當我們在字串緩衝去被多個執行緒使用是,JVM不能保證StringBuilder的操作是安全的,雖然他的速度最快,但是可以保證StringBuffer是可以正確操作的。當然大多數情況下就是我們是在單執行緒下進行的操作,所以大多數情況下是建議用StringBuilder而不用StringBuffer的,就是速度的原因。
5、使用策略
(1)基本原則:如果要操作少量的資料,用String ;單執行緒操作(字串緩衝區下操作)大量資料,用StringBuilder ;多執行緒操作(字串緩衝區下操作)大量資料,用StringBuffer。
(2)不要使用String類的"+"來進行頻繁的拼接,因為那樣的效能極差的,應該使用StringBuffer或StringBuilder類,這在Java的優化上是一條比較重要的原則。例如:
String result = "";
for (String s : hugeArray) {
result = result + s;
}
// 使用StringBuilder
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
sb.append(s);
}
String result = sb.toString();
當出現上面的情況時,顯然我們要採用第二種方法,因為第一種方法,每次迴圈都會建立一個String result用於儲存結果,除此之外二者基本相同(對於jdk1.5及之後版本)。
(3)為了獲得更好的效能,在構造 StringBuffer 或 StringBuilder 時應儘可能指定它們的容量。當然,如果你操作的字串長度(length)不超過 16 個字元就不用了,當不指定容量(capacity)時預設構造一個容量為16的物件。不指定容量會顯著降低效能。
(4)StringBuilder一般使用在方法內部來完成類似"+"功能,因為是執行緒不安全的,所以用完以後可以丟棄。StringBuffer主要用在全域性變數中。
(5)相同情況下使用 StringBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的效能提升,但卻要冒多執行緒不安全的風險。而在現實的模組化程式設計中,負責某一模組的程式設計師不一定能清晰地判斷該模組是否會放入多執行緒的環境中執行,因此:除非確定系統的瓶頸是在 StringBuffer 上,並且確定你的模組不會執行在多執行緒模式下,才可以採用StringBuilder;否則還是用StringBuffer。