JAVA的String、StringBuilder和StringBuffer類的區別,java清空stringbuffer
StringBuffer類(或者StringBuilder)和String一樣,也用來代表字串,只是由於StringBuffer的內部實現方式和String不同,所以StringBuffer在進行字串處理時,不產生新的對象,在記憶體使用量上要優於String類。
所以在實際使用時,如果經常需要對一個字串進行修改,例如插入、刪除等操作,使用StringBuffer要更加適合一些。
在StringBuffer類中存在很多和String類一樣的方法,這些方法在功能上和String類中的功能是完全一樣的。
但是有一個最顯著的區別在於,對於StringBuffer對象的每次修改都會改變對象自身,這點是和String類最大的區別。
另外由於StringBuffer是安全執行緒的,關於線程的概念後續有專門的章節進行介紹,所以在多線程程式中也可以很方便的進行使用,但是程式的執行效率相對來說就要稍微慢一些。
1、StringBuffer對象的初始化
StringBuffer對象的初始化不像String類的初始化一樣,Java提供的有特殊的文法,而通常情況下一般使用構造方法進行初始化。
例如:
StringBuffer s = new StringBuffer();
這樣初始化出的StringBuffer對象是一個空的對象。
如果需要建立帶有內容的StringBuffer對象,則可以使用:
StringBuffer s = new StringBuffer(“abc”);
這樣初始化出的StringBuffer對象的內容就是字串”abc”。
需要注意的是,StringBuffer和String屬於不同的類型,也不能直接進行強制類型轉換,下面的代碼都是錯誤的:
StringBuffer s = “abc”; //賦實值型別不匹配tringBuffer s = (StringBuffer)”abc”; //不存在繼承關係,無法進行強轉StringBuffer對象和String對象之間的互轉的代碼如下: String s = “abc”; StringBuffer sb1 = new StringBuffer(“123”); StringBuffer sb2 = new StringBuffer(s); //String轉換為StringBuffer String s1 = sb1.toString(); //StringBuffer轉換為String
2、StringBuffer的常用方法
StringBuffer類中的方法主要偏重於對於字串的變化,例如追加、插入和刪除等,這個也是StringBuffer和String類的主要區別。
a、append方法
public StringBuffer append(boolean b)
該方法的作用是追加內容到當前StringBuffer對象的末尾,類似於字串的串連。調用該方法以後,StringBuffer對象的內容也發生改變,例如:
StringBuffer sb = new StringBuffer(“abc”);
sb.append(true);
則對象sb的值將變成”abctrue”。
使用該方法進行字串的串連,將比String更加節約內容,例如應用於資料庫SQL語句的串連,例如:
StringBuffer sb = new StringBuffer();String user = “test”;String pwd = “123”;sb.append(“select * from userInfo where username=“) .append(user) .append(“ and pwd=”) .append(pwd);
這樣對象sb的值就是字串“select * from userInfo where username=test and pwd=123”。
b、deleteCharAt方法
public StringBuffer deleteCharAt(int index)
該方法的作用是刪除指定位置的字元,然後將剩餘的內容形成新的字串。例如:
StringBuffer sb = new StringBuffer(“Test”);sb. deleteCharAt(1);
該代碼的作用刪除字串對象sb中索引值為1的字元,也就是刪除第二個字元,剩餘的內容組成一個新的字串。所以對象sb的值變為”Tst”。
還存在一個功能類似的delete方法:
public StringBuffer delete(int start,int end)
該方法的作用是刪除指定區間以內的所有字元,包含start,不包含end索引值的區間。例如:
StringBuffer sb = new StringBuffer(“TestString”);sb. delete (1,4);
該代碼的作用是刪除索引值1(包括)到索引值4(不包括)之間的所有字元,剩餘的字元形成新的字串。則對象sb的值是”TString”。
c、insert方法
public StringBuffer insert(int offset, boolean b)
該方法的作用是在StringBuffer對象中插入內容,然後形成新的字串。例如:
StringBuffer sb = new StringBuffer(“TestString”);sb.insert(4,false);
該範例程式碼的作用是在對象sb的索引值4的位置插入false值,形成新的字串,則執行以後對象sb的值是”TestfalseString”。
d、reverse方法
public StringBuffer reverse()
該方法的作用是將StringBuffer對象中的內容反轉,然後形成新的字串。例如:
StringBuffer sb = new StringBuffer(“abc”);sb.reverse();
轉以後,對象sb中的內容將變為”cba”。
e、setCharAt方法
public void setCharAt(int index, char ch)
該方法的作用是修改對象中索引值為index位置的字元為新的字元ch。例如:
StringBuffer sb = new StringBuffer(“abc”);
sb.setCharAt(1,’D’);
則對象sb的值將變成”aDc”。
f、trimToSize方法
public void trimToSize()
該方法的作用是將StringBuffer對象的中儲存空間縮小到和字串長度一樣的長度,減少空間的浪費。
總之,在實際使用時,String和StringBuffer各有優勢和不足,可以根據具體的使用環境,選擇對應的類型進行使用。
String和StringBuffer的效率對比
為了更加明顯地看出它們的執行效率,下面的代碼,將26個英文字母加了10000次。
public class Demo { public static void main(String[] args){ String fragment = "abcdefghijklmnopqrstuvwxyz"; int times = 10000; // 通過String對象 long timeStart1 = System.currentTimeMillis(); String str1 = ""; for (int i=0; i<times; i++) { str1 += fragment; } long timeEnd1 = System.currentTimeMillis(); System.out.println("String: " + (timeEnd1 - timeStart1) + "ms"); // 通過StringBuffer long timeStart2 = System.currentTimeMillis(); StringBuffer str2 = new StringBuffer(); for (int i=0; i<times; i++) { str2.append(fragment); } long timeEnd2 = System.currentTimeMillis(); System.out.println("StringBuffer: " + (timeEnd2 - timeStart2) + "ms"); }}
運行結果:
String: 5287msStringBuffer: 3ms
結論很明顯,StringBuffer的執行效率比String快上千倍,這個差異隨著疊加次數的增加越來越明顯,當疊加次數達到30000次的時候,運行結果為:
String: 35923ms
StringBuffer: 8ms
所以,強烈建議在涉及大量字串操作時使用StringBuffer。
StringBuilder類
StringBuilder類和StringBuffer類功能基本相似,方法也差不多,主要區別在於StringBuffer類的方法是多安全執行緒的,而StringBuilder不是安全執行緒的,相比而言,StringBuilder類會略微快一點。
StringBuffer、StringBuilder、String中都實現了CharSequence介面。
CharSequence是一個定義字串操作的介面,它只包括length()、charAt(int index)、subSequence(int start, int end) 這幾個API。
StringBuffer、StringBuilder、String對CharSequence介面的實現過程不一樣,如所示:
可見,String直接實現了CharSequence介面;StringBuilder 和 StringBuffer都是可變的字元序列,它們都繼承於AbstractStringBuilder,實現了CharSequence介面。
jdk的實現中StringBuffer與StringBuilder都繼承自AbstractStringBuilder,對於多線程的安全與非安全看到StringBuffer中方法前面的一堆synchronized就大概瞭解了。
我們知道使用StringBuffer等無非就是為了提高java中字串串連的效率,因為直接使用+進行字串串連的話,jvm會建立多個String對象,因此造成一定的開銷。
AbstractStringBuilder中採用一個char數組來儲存需要append的字串,char數組有一個初始大小,當append的字串長度超過當前char數組容量時,則對char數組進行動態擴充,也即重新申請一段更大的記憶體空間,然後將當前char數組拷貝到新的位置,因為重新分配記憶體並拷貝的開銷比較大,所以每次重新申請記憶體空間都是採用申請大於當前需要的記憶體空間的方式,這裡是2倍。
分類總結如下:
(1)StringBuffer在進行追加操作的時候,只是在字串的後邊進行追加操作,追加的過程也是需要進行判斷預存數組大小是否超過一開始設定的16子字元,然後才可以進行追加,每次結果都會對 StringBuffer 對象本身進行操作,而不是產生新的對象,再改變對象引用。;由於StringBuilder和StringBuffer本身都是繼承自AbstractStringBuilder,在進行追加操作的時候,使用的就是AbstractStringBuilder類中的append方法,中展示了AbstractStringBuilder類中所有重載的append方法,根據不同的需求可以實現不同的功能,
以append(CharSequence s)為例,展示一下:(下邊的方法都是AbstractStringBuilder中的方法)
@Override public AbstractStringBuilder append(CharSequence s) { if (s == null) return appendNull(); if (s instanceof String) return this.append((String)s); if (s instanceof AbstractStringBuilder) return this.append((AbstractStringBuilder)s); return this.append(s, 0, s.length()); }
首先進行判斷是否為空白,然後判斷是哪一個執行個體,最後掉用append另一個重載的方法:
@Override public AbstractStringBuilder append(CharSequence s, int start, int end) { if (s == null) s = "null"; if ((start < 0) || (start > end) || (end > s.length())) throw new IndexOutOfBoundsException( "start " + start + ", end " + end + ", s.length() " + s.length()); int len = end - start; ensureCapacityInternal(count + len); for (int i = start, j = count; i < end; i++, j++) value[j] = s.charAt(i); count += len; return this; }
其中:
/** * The count is the number of characters used. */ int count; /** * The value is used for character storage. */ char[] value;
可以看出:ensureCapacityInternal(count + len); 是一個擴容的過程,value[j] = s.charAt(i); 是儲存追加字串的過程,具體的過程就是再原StringBuffer對象的基礎上首先通過擴容,之後在進行追加操作;
(2)StringBuffer的四個構造方法如下:
StringBuffer() 構造一個其中不帶字元的字串緩衝區,其初始容量為 16 個字元。StringBuffer(CharSequence seq) public java.lang.StringBuilder(CharSequence seq) 構造一個字串緩衝區,它包含與指定的 CharSequence 相同的字元。StringBuffer(int capacity) 構造一個不帶字元,但具有指定初始容量的字串緩衝區。StringBuffer(String str) 構造一個字串緩衝區,並將其內容初始化為指定的字串內容
每個字串緩衝區都有一定的容量。只要字串緩衝區所包含的字元序列的長度沒有超出此容量,就無需分配新的內部緩衝區數組。如果內部緩衝區溢位,則此容量自動增大。從 JDK 5 開始,為該類補充了一個單個線程使用的等價類別,即 StringBuilder。與該類相比,通常應該優先使用 StringBuilder 類,因為它支援所有相同的操作,但由於它不執行同步,所以速度更快。
(3)而對於String來說,雖然追加(字串串聯)的本質也是通過建立StringBuilder對象,通過StringBuilder的append方法來實現的,但是每次追加的過程都是一次建立StringBuilder對象的過程,因也就是說在每次對 String 類型進行改變的時候其實都等同於產生了一個新的 String 對象,然後將指標指向新的 String 對象,效率顯然要比直接使用StringBuilder來說要低很多,網上也有很多測試的實驗,也充分說明這一點,所以經常改變內容的字串最好不要用 String ,因為每次產生對象都會對系統效能產生影響,特別當記憶體中無引用對象多了以後, JVM 的 GC 就會開始工作,那速度是一定會相當慢的;
(4)而在某些特別情況下, 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 會規規矩矩的按照原來的方式去做, S1 對象的產生速度就不像剛才那麼快了,一會兒我們可以來個測試作個驗證。
由此我們得到第一步結論: 在大部分情況下 StringBuffer > String
(4)總的來說,StringBuilder適合於需要進行經常性的字元追加刪除操作等,String本身設計的初衷應該是為了在後邊的使用過程種,不會進行太多的增刪操作,畢竟String是final類型的,是不可變的對象,因此他們都有所擅長的地方,根據自己的需求進行合理的選擇即可;
總結
安全執行緒:
StringBuffer:安全執行緒
StringBuilder:線程不安全
速度:
一般情況下,速度從快到慢為 StringBuilder > StringBuffer > String,當然這是相對的,不是絕對的。
使用環境:
操作少量的資料使用 String;
單線程操作大量資料使用 StringBuilder;
多線程操作大量資料使用 StringBuffer。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。