String、StringBuffer、StringBuilder的問題

       

          無論做項目中還是測試中都經常用到String,StringBuffer,StringBuilder,大家都知道,String可以用“+”來對字符串進行拼接,StringBuffer和StringBuilder使用append進行拼接;但使用“+”來進行字符串連接可能會寫出效率很低的代碼,使用“+”越多,在內存中的String實例越多,所花費的用於管理的時間也越多;

          在面向對象程序設計中,最好是能重複運用已生成的對象,對象的生成需要內存空間與時間,不斷地產生String實例是一個沒有效率的行爲;

         String 類型和StringBuffer的主要性能區別:String是不可變的對象, 因此在每次對String 類型進行改變的時候,都會生成一個新的 String 對象,然後將指針指向新的 String 對象,所以經常改變內容的字符串最好不要用 String ,因爲每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了以後, JVM 的 GC 就會開始工作,性能就會降低。

         使用 StringBuffer 類時,每次都會對 StringBuffer 對象本身進行操作,而不是生成新的對象並改變對象引用。所以多數情況下推薦使用 StringBuffer ,特別是字符串對象經常改變的情況下。

         當我們在字符串緩衝區被多個線程使用時,JVM不能保證StringBuilder的操作是安全的,雖然他的速度最快,但是可以保證StringBuffer是可以正確操作的。當然大多數情況下就是我們是在單線程下進行的操作,所以大多數情況下是建議用StringBuilder而不用StringBuffer的,就是速度的原因,JDK 文檔均推薦使用StringBuilder;

 另外還有,String,StringBuffer,StringBuilder這三者還有一些區別就是這三者均以值參數形式傳遞,只不過在傳遞過程中,Sting仍然以值傳遞,調用方法後其值不會發生變化,而StringBuffer,StringBuilder耍了點詭計,以值對象傳遞,調用方法後其值會發生變化。這點符合JAVA的相關設計規範。以下以代碼範例說明這一點;    

        

而在某些特別情況下, 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 會規規矩矩的按照原來的方式去做;


程序1:

public class Test2
{
	public static void println(String str, StringBuffer strBuffer,
			StringBuilder strBuilder)
	{
		str = "以String方式輸出";
		strBuffer.append("以StringBuffer方式輸出");
		strBuilder.append("以StringBuilder方式輸出");
	}

	public static void main(String[] args)
	{
		String str = "字符串";

		StringBuffer strBuffer = new StringBuffer();
		strBuffer.append("字符串");
		StringBuilder strBuilder = new StringBuilder();
		strBuilder.append("字符串");

		System.out.println("調用前str=" + str + ",   strBuffer=" + strBuffer + ",   strBuilder=" + strBuilder);
		println(str, strBuffer, strBuilder);
		System.out.println("調用後str=" + str + ",   strBuffer=" + strBuffer + ",   strBuilder=" + strBuilder);
	}
}

調用前str=字符串,   strBuffer=字符串,   strBuilder=字符串
調用後str=字符串,   strBuffer=字符串以StringBuffer方式輸出,   strBuilder=字符串以StringBuilder方式輸出

string中"+"實現字符串連接分析:

public class PlusTest
{

	public static void main(String[] args)
	{
		String a = "a";
		String b = "b";
		String c = a + b;
		String d = c + 1;
	}
}
將上述代碼編譯後再反編譯,得到的代碼:

public class PlusTest  
{  
  
    public PlusTest()  
    {  
    }  
  
    public static void main(String args[])  
    {  
        String a = "a";  
        String b = "b";  
        String c = (new StringBuilder(String.valueOf(a))).append(b).toString();  
        String d = (new StringBuilder(String.valueOf(c))).append(1).toString();  
    }  
}  
通過反編譯的結果可以看出,"+"實現字符串連接的過程中,實際上是藉助了StringBuilder類的append方法,每拼接一次都要實例化一個StringBuilder對象,效率低是必然的;

建議:

     (1)爲了獲得更好的性能,在構造 StirngBuffer 或 StirngBuilder 時應儘可能指定它的容量。當然,如果你操作的字符串長度不超過 16 個字符就不用了;J2SE 5.0提供java.lang.StringBuilder類,使用這個類所產生的對象默認會有16個字符的長度,您也可以自行指定初始長度。如果附加的字符超出可容納的長度,則StringBuilder對象會自動增加長度以容納被附加的字符;

     (2)相同情況下使用 StirngBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。而在現實的模塊化編程中,負責某一模塊的程序員不一定能清晰地判斷該模塊是否會放入多線程的環境中運行,因此:除非你能確定你的系統的瓶頸是在 StringBuffer 上,並且確定你的模塊不會運行在多線程模式下,否則還是用 StringBuffer 吧;

     (3)用好現有的類比引入新的類更重要。很多程序員在使用 StringBuffer 時是不指定其容量的(至少我見到的情況是這樣),如果這樣的習慣帶入 StringBuilder 的使用中,你將只能獲得 10 %左右的性能提升(不要忘了,你可要冒多線程的風險噢);但如果你使用指定容量的 StringBuffer,你將馬上獲得 45% 左右的性能提升,甚至比不使用指定容量的StringBuilder 都快 30% 左右。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章