StringBuilder竟然是基於鏈表而不是數組的集合,它不是2被的增加容量,而是新增一個StringBuilder節點,容量為int num = Math.Max(minBlockCharCount, Math.Min(this.Length, 8000));minBlockCharCount:填滿上個節點後剩餘的字元個數;this.Length:上兩個節點的容量之和新增的資料往裡面填,基本上每個節點的最大容量就是8000個字元,滿了之後再新增節點,不會拷貝以前的資料。StringBuilder有一個名為m_ChunkPrevious的欄位,指向上一個節點。不知道這是新的實現方式還是以前就是這樣實現的,
對於一直往裡面Append資料效率的確是快了,不會拷貝以前的資料,其他的操作實現起來就麻煩一點,至於效率估計會有點影響。這樣的實現方式對於添加大量的字串效率會有很大的提高,如果是用一個連續的數組存放字串的話,在複製資料的時候效能會有點影響。
String關於字串,我想大家最喜歡比較+、+=、String.Format、String.Concat隨的效能好,如果只是一條或是少量順序語句,說誰的效能好沒有任何意義,根本體現不出誰的效能好,要比效能就得大量的資料測試,在迴圈次數相同的情況下,他們幾個的效能真的差不多,如果非要比速度的話,”+”>“+=“>“ConCat”>“Format”並不是大家常說的+=的效能比+好(不一定總是這個結果)。其實要提高效能就得減少迴圈的次數,在以同樣的方式減少迴圈的次數的情況下,效能提高了,基本上是效能跟迴圈的次數成反比,效能差不多。我們來看看他們的彙編代碼就知道了,我們看出+與+=執行的指令條數是一樣的,ConCat比Format少了幾個移動指標的指令,其他的指令個數都是一樣的,當然他們的記憶體回收次數也就是一樣的。總之少量字串用他們哪一個都無所謂,畢竟不涉及效能問題,我喜歡用Format,因為它讓代碼更好理解,看著舒服些,大量字串就必須得用StringBuilder。
測試代碼:
View Code
static void Test1() { int timer = 100000; using (new OperationTimer("+=")) { string s1 = string.Empty; for (int i = 0; i < timer; i++) { s1 += i.ToString(); } } using (new OperationTimer("+")) { string s1 = string.Empty; for (int i = 0; i < timer; i++) { s1 = s1 + i.ToString(); } } using (new OperationTimer("concat")) { string s1 = string.Empty; for (int i = 0; i < timer; i++) { s1 = string.Concat(s1, i.ToString()); } } using (new OperationTimer("format")) { string format = "{0}"; string s1 = string.Empty; for (int i = 0; i < timer; i++) { s1 = s1 + string.Format(format, i.ToString()); } } } static void Test2() { int timer = 110000; using (new OperationTimer("+")) { string s1 = string.Empty; for (int i = 0; i < timer; i+=2) { s1 = s1 + i.ToString()+(i+1).ToString(); } } using (new OperationTimer("+=")) { string s1 = string.Empty; for (int i = 0; i < timer; i += 2) { s1 += "i" + i.ToString() + (i + 1).ToString(); } } using (new OperationTimer("concat")) { string s1 = string.Empty; for (int i = 0; i < timer; i += 2) { s1 = string.Concat(s1, i.ToString(), (i + 1).ToString()); } } using (new OperationTimer("format")) { string format = "{0}{1}"; string s1 = string.Empty; for (int i = 0; i < timer; i += 2) { s1 = s1 + string.Format(format, i.ToString(), (i + 1).ToString()); } } }
彙編代碼如下:
測試結果:
ArrayList你可以拋棄它了,用List吧,向ArrayList中添加實值型別資料的資料效能沒有List的好,參考型別效能差不多。實值型別ArrayList會出現裝箱和拆箱,效能差點,而參考型別大家調用的都是指標,List就別認為自己長得帥了,List會為每種實值型別都會產生一套代碼,而參考型別是同一套代碼。其實以前我也不知道為什麼會是這樣,在我看了C++的模版後我就明白了,模版的參數可以是任何類型,為每種類型產生一套代碼,但是C++有模版半特化功能,就是將模版的參數加一個約束,這種模版的參數必須是指標類型,有了這兩個模版,實值型別的就會調用第一個模版,產生一套代碼,因為每種實值型別所佔用的位元組不是都一樣,指標類型的就會調用特化的模版,因為每種指標佔用的都是4個位元組,所以根本不需要為每種類型都產生一套代碼。代碼的膨脹問題就是這樣解決的。List容量基本上是2倍2倍的擴增,然後將以前的資料複製過來,實值型別直接複製資料,參考型別直接複製對象的引用,如果對象被修改了,List中的引用也就被修改了,當然參考型別都是這樣的。
Hashtable也是過時了,被Dictionary取代了,非泛型的集合肯定是會被泛型的取代的。他們的實現原理我想應該是差不多的,對於雜湊字典,我認為瞭解他是怎麼散列的就行,每種集合的提共的方法其實都是都差不多,只是最主要的演算法不同而已。Object有個GetHashCode()方法,這個方法能為每個對象產生一個不同的長度為10的整數,然後將這個數散列到一個數組中,數組是按照質數的順序擴充的,也就是說,數組的長度都是質數,這主要是為了均勻的散列。如果向Dictionary中添加兩個相同的Key,也就是散列到同一個位置就會跑出異常, HashSet也是基於散列的,當有多個對象散列到同一個地方的時候,它不會跑出異常,而是直接返回。排除一個數組或是多個數組中的重複資料用HashSet是個不錯的選擇。
Stack棧很簡單,以2倍的方式擴容,進棧的相當於填充數組,從0~N-1,出棧的時候將最後加入棧中的元素清掉,也就是後進先出。
Queue隊列也很簡單,預設以2被的方式擴容,擴容的倍數可以自訂,進隊列的時候相當於填充數組,從0~N-1,出隊列就是返回一個索引所知的元素,每有一個元素出隊列,這個索引就增1,出隊列的元素清除,元素佔用的位置保留,即數組的大小不變,只是你訪問不了。
SortDictionary是基於紅/黑樹狀結構的,這個紅/黑樹狀結構還有點難理解,還在學習中,就不多說了。那就是尋找速度快,時間複雜度為O(logN),在添加元素的時候會經常調整樹的結構。
作者:陳太漢
部落格:http://www.cnblogs.com/hlxs/