string和stringbuilder的解剖

 

StringStringBuilder的深刻解析html

 

前言:本文出發點是咱們開發的過程當中是否真正的理解stringbuilder的使用,string字符串操做的是如何實現(哈希表),stringbuilder是否設置默認值容量,何時才用stringbuilder。安全

 

一律念Stringstringbulider的理解app

 

string是咱們用的最多的類型之一,是一個特殊的引用類型,直接派生於Object,所以它的值儲存在託管堆上。構造一個新字符串的時候,不須要用new。它是」不可變的「。初始化字符串對象後,該字符串對象的長度、內容都是肯定不變的了。能夠思考這個時候,咱們須要更改或者添加字符串,會作一個怎樣的動做呢?ide

 

   StringBulider由於string的」不可變「,致使屢次修改字符串的時候會損耗性能,.net爲了解決這個問題,提供了動態建立string的方法,以克服string不可變帶來的性能損耗。StringBuilder和String比起來,功能較少,只有基本的屬性和增刪改的方法。可是你知道stringbuilder也有一個固定的容量值嗎??注意:StringBulider容量  (默認是16雖然   StringBuilder   對象是動態對象,容許擴充它所封裝的字符串中字符的數量,可是您能夠爲它可容納的最大字符數指定一個值。此值稱爲該對象的容量,不該將它與當前   StringBuilder   對象容納的字符串長度混淆在一塊兒。例如,能夠建立   StringBuilder   類的帶有字符串「Hello」(長度爲   5)的一個新實例,同時能夠指定該對象的最大容量爲   25。當修改   StringBuilder   時,在達到容量以前,它不會爲其本身從新分配空間。當達到容量時,將自動分配新的空間且容量翻倍。可使用重載的構造函數之一來指定StringBuilder類的容量。如下代碼示例指定能夠將   MyStringBuilder對象擴充到最大25個空白。 StringBuilder MyStringBuilder  =  new   StringBuilder("Hello   World!",   25);   另外,能夠使用讀/寫   Capacity   屬性來設置對象的最大長度。如下代碼示例使用   Capacity   屬性來定義對象的最大長度。  MyStringBuilder.Capacity   =   25;   EnsureCapacity   方法可用來檢查當前   StringBuilder   的容量。若是容量大於傳遞的值,則不進行任何更改;可是,若是容量小於傳遞的值,則會更改當前的容量以使其與傳遞的值匹配。 也能夠查看或設置   Length   屬性。若是將   Length   屬性設置爲大於   Capacity   屬性的值,則自動將   Capacity   屬性更改成與   Length   屬性相同的值。若是將   Length   屬性設置爲小於當前   StringBuilder   對象內的字符串長度的值,則會縮短該字符串。函數

 

、爲何說變更影響性能。(stringStringBuilder性能

 String:string s = "I am ";s += "Sky";怎麼分配內存的呢?

 

  備註:若是每次都這樣從新分配真實瘋了,.net確定沒有那麼傻了,最起碼要避免下若是兩個string的字符串如出一轍,我是否是不須要分配新的堆,只須要制定一樣的引用就行了呢?下面就出現了一個名詞:字符串留用,CLR初始化的時候會建立哈希表,每構建一個新字符串都會與哈希表匹配,查找是否有相同的字符串,若是匹配,就會返回這個已存在的舊對象,由新變量進行引用。不然,就會建立一個字符串副本添加到哈希表裏,Key就是字符串,Value就是string對象在堆上的地址。測試

 

是否是全部的都是這樣呢,有什麼特殊狀況嗎?ui

 

總結New出來的對象是不會記錄在哈希表。.net

tringBuilder 對象維護一個緩衝區,以便容納新數據的串聯。若是有足夠的空間,新數據將被追加到緩衝區的末尾;不然,將分配一個新的、更大的緩衝區,原始緩衝區中的數據被複制到新的緩衝區,而後將新數據追加到新的緩衝區。pwa

 

 

內部實現原理

 

總結:StringBuffer是可變類,和線程安全的字符串操做類,任何對它指向的字符串的操做都不會產生新的對象。 每一個StringBuffer對象都有必定的緩衝區容量,當字符串大小沒有超過容量時,不會分配新的容量,當字符串大小超過容量時,會自動增長容量。事實是,StringBuilder比string快的緣由是string拼接時產生了中間對象,最終是垃圾。如: string str = "a";str += "b";str += "c";那麼,最終結果是"abc",但第二行產生了"ab"只是一箇中間對象,是個垃圾。用StringBuilder會避免這種中間對象的產生。那若是我這麼寫呢? string str ="a"+"b"+ "c";這會比StringBuilder慢嗎?不會。

中間對象的產生纔是影響性能的主要緣由。

 

3、測試案例:

 

  private void button1_Click(object sender, EventArgs e)

        {

 

            int number =int.Parse( textBox1.Text.ToString());

 

            GetStringTime(number);

            GetStringBulider(number);

 

            GetStringTime1(number);

            GetStringBulider1(number);

        }

 

        /// <summary>

        ///  測試string 性能時間

        /// </summary>

        private void GetStringTime(int number)

        {

            Stopwatch watch = new Stopwatch();

            List< String> li = new List< string>();

            watch.Start();

            string str = "select * from testa inner join 快速排序耗費時間 快速排序耗費時間 快速排序耗費時間";

            for (int i = 0; i < number; i++)

            {

                li.Add(str);    

            }

            watch.Stop();

            label1.Text = watch.ElapsedMilliseconds.ToString();

 

        }

 

 

        private void GetStringBulider(int number)

        {

            Stopwatch watch = new Stopwatch();

            List<String> li = new List<string>();

            watch.Start();

            StringBuilder strb = new StringBuilder();

            strb.Append("select * from testa inner join dsadfasfsadfa快速排序耗費時間快速排序耗費時間");

            for (int i = 0; i < number; i++)

            {

                li.Add(strb.ToString());   

            }

            watch.Stop();

            label2.Text = watch.ElapsedMilliseconds.ToString();

       

        }

 

 

        /// <summary>

        ///  測試string 性能時間變化

        /// </summary>

        private void GetStringTime1(int number)

        {

            Stopwatch watch = new Stopwatch();

            List<String> li = new List<string>();

            watch.Start();

            string str = "select * from testa inner join 快速排序耗費時間 快速排序耗費時間 快速排序耗費時間";

            for (int i = 0; i < number; i++)

            {

                str = str+"select * from testa inner join 快速排序耗費時間 快速排序耗費時間 快速排序耗費時間";

          

            }

            watch.Stop();

            label1.Text =label1.Text+"不變,變化"+  watch.ElapsedMilliseconds.ToString();

 

        }

 

 

        /// <summary>

        ///  測試stringBulider  變化的性能

        /// </summary>

        /// <param name="number"></param>

        private void GetStringBulider1(int number)

        {

            Stopwatch watch = new Stopwatch();

            List<String> li = new List<string>();

            watch.Start();

            StringBuilder strb = new StringBuilder();

            strb.Append("select * from testa inner join dsadfasfsadfa快速排序耗費時間快速排序耗費時間");

            for (int i = 0; i < number; i++)

            {

                strb.Append("select * from testa inner join dsadfasfsadfa快速排序耗費時間快速排序耗費時間");

   

            }

            watch.Stop();

            label2.Text =label2.Text+"不變,變化"+ watch.ElapsedMilliseconds.ToString();

 

        }

 

效果圖以下:

 

 

 

 

備註:每圖第一行表示string,第二行表示stringbulider,變化表示str++的意思或append。

四:總結:String stringbulider的總體彙總。

1. Sting是恆定的,string部裏的人是可變化的。

2. 對於簡單的字符串鏈接操做,在性能上stringbuilder不必定老是優於string。由於stringbulider對象的建立也消耗大量的性能,在字符串鏈接比較少的狀況下,過分濫用stringbuilder會致使性能的浪費而非節約,只有大量沒法預知次數的字符串操做才考慮stringbuilder的使用。從最後分析能夠看出若是是100行之內根本看不出太大差異。

3. Stringbulider的使用,最好制定合適的容量值,不然優於默認值容量不足而頻繁的進行內存分配操做,是不妥的實現方法。

 

 

 能夠深思,第一咱們對適合的容量值處理了嗎? 第二,咱們是否是一再提要使用stringbuilder說性能好,可是在100行內的字符操做有分別嗎。

簡單的字符串鏈接操做能夠適度思考下 string.Concat 和 string.join 的使用。(string.concat的裝箱操做)。

 

 

參考文章:

http://www.cnblogs.com/juqiang/archive/2005/04/19/140538.html

http://www.cnblogs.com/huangxincheng/p/4042105.html

http://www.cnblogs.com/heartstill/archive/2011/11/11/2245411.html

http://www.cnblogs.com/kid-li/archive/2006/10/18/532174.html

http://www.cnblogs.com/gfwei/archive/2007/03/14/674499.html

http://www.cnblogs.com/skychen1218/p/3593678.html

書籍:《你必須知道的.net》什麼是string345頁)

相關文章
相關標籤/搜索