C#集合
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),在添加元素的時候會經常調整樹的結構。
作者:陳太漢

浙公網安備 33010602011771號