今天有空正好把泛型和非泛型集合類寫了一個比較,並做了個的例程,可以比較清楚的理解它們概念和使用方法。
泛型與非泛型集合類在C#程序中是非常重要的一個基礎概念,這里列一個表來進行對比:
非泛型集合類 |
泛型集合類 |
描述 |
ArrayList |
List<T> |
表示具有動態大小的對象數組 |
Hashtable |
Dictionary<Tkey,Tvalue> |
由鍵值對組成的集合 |
SortedList |
SortedList<Tkey,Tvalue> |
和字典相似但有排序功能的集合 |
Queue |
Queue<T> |
表示標准的先進先出(FIFO)隊列 |
Stack |
Stack<T> |
后進先出(LIFO)隊列,提供壓入和彈出功能 |
泛型與非泛型集合類在概念和功能上各有不同,其中非泛型集合類在取出值時需要進行類型的轉換操作,如果加入值類型會引起裝箱和拆箱的操作,這會帶來巨大的性能額外開銷,如果掌握好泛型數組之后可以不再需要用非泛型的數組了,同時帶來類型安全的好處並減少在值類型和引用類型之間的裝箱和拆箱。
下面做一個例程來演示一下,
例程是順手在WPF下做的,雖然不是C#winform,但差不多是一回事,界面代碼就不貼了。
先做一個學生類:

public class student { public int Number { get; set; } public string Name { get; set; } public bool Sex { get; set; } public student(int _number, string _name, bool _sex) { Number = _number; Name = _name; Sex = _sex; } public override string ToString() { return string.Format("序號:{0},姓名:{1},性別:{2}", Number.ToString(), Name, Sex ? "男" : "女"); } }
一、ArrayList與List<T>示例

ArrayList arrayStudents = new ArrayList(); List<student> listStudnets = new List<student>(); private void Button_Click(object sender, RoutedEventArgs e) { addData0(); showExemple0(); } private void addData0() { arrayStudents.Add(new student(1, "小顆豆一", true)); arrayStudents.Add(new student(3, "小顆豆二", false)); arrayStudents.Add(new student(5, "小顆豆三", true)); arrayStudents.Add(new student(2, "小顆豆四", false)); arrayStudents.Add(new student(4, "小顆豆五", true)); arrayStudents.Add(new student(6, "小顆豆六", false)); arrayStudents.Add("這里冒一個字符串,需要轉換,如果這里是值類型還要進行裝箱與拆箱,帶來額外的開銷!"); listStudnets.Add(new student(1, "小顆豆一", true)); listStudnets.Add(new student(3, "小顆豆二", false)); listStudnets.Add(new student(5, "小顆豆三", true)); listStudnets.Add(new student(2, "小顆豆四", false)); listStudnets.Add(new student(4, "小顆豆五", true)); listStudnets.Add(new student(6, "小顆豆六", false)); } private void showExemple0() { richTextBox1.AppendText("--------ArrayList與List<T>示例--------\r\n"); richTextBox1.AppendText("--------非泛型數組的操作(需要強制轉換)--------\r\n"); foreach (var item in arrayStudents) { if (item is student) richTextBox1.AppendText(item.ToString() + "\r\n"); else richTextBox1.AppendText((string)item + "\r\n"); } richTextBox1.AppendText("--------泛型數組的操作(不需要強制轉換)--------\r\n"); foreach (var item in listStudnets) { richTextBox1.AppendText(item.ToString() + "\r\n"); } }
注意觀察代碼中ArrayList接收的值包括類和字符串,所以要有不同的強制轉換,雖然正常運行,但這樣帶來了安全隱患,泛型集合不需要轉換可以輕松調用學生類中自定義的.ToString(),運行效果如下圖:
二、Hashtable與Dictionary<Tkey,Tvalue>示例

private void button1_Click(object sender, RoutedEventArgs e) { richTextBox1.Document.Blocks.Clear(); addData1(); showExemple1(); } Hashtable hashSudents = new Hashtable(); Dictionary<string ,student> DictStudents = new Dictionary<string ,student>(); private void addData1() { hashSudents.Add("序號1", new student(1, "小顆豆一", true)); hashSudents.Add("序號2", new student(3, "小顆豆二", false)); hashSudents.Add("序號3", new student(5, "小顆豆三", true)); hashSudents.Add("序號4", new student(2, "小顆豆四", false)); hashSudents.Add("序號5", new student(4, "小顆豆五", true)); hashSudents.Add("序號6", new student(6, "小顆豆六", false)); DictStudents.Add("序號1", new student(1, "小顆豆一", true)); DictStudents.Add("序號2", new student(3, "小顆豆二", false)); DictStudents.Add("序號3", new student(5, "小顆豆三", true)); DictStudents.Add("序號4", new student(2, "小顆豆四", false)); DictStudents.Add("序號5", new student(4, "小顆豆五", true)); DictStudents.Add("序號6", new student(6, "小顆豆六", false)); } private void showExemple1() { richTextBox1.AppendText("--------Hashtable與Dictionary<Tkey,Tvalue>示例--------\r\n"); richTextBox1.AppendText("--------非泛型數組的操作(需要強制轉換),注意順序是特定的------\r\n"); foreach (DictionaryEntry item in hashSudents) { richTextBox1.AppendText(item.Key.ToString() + ((student)item.Value).ToString() + "\r\n"); } richTextBox1.AppendText("包含序號2=" + hashSudents.ContainsKey("序號2").ToString() + "\r\n"); richTextBox1.AppendText("包含序號9=" + hashSudents.ContainsKey("序號9").ToString() + "\r\n"); richTextBox1.AppendText("--------泛型數組的操作(不需要強制轉換),順序是原來的順序-----\r\n"); foreach (KeyValuePair<string, student> item in DictStudents) { richTextBox1.AppendText(item.Key.ToString() + item.Value.ToString() + "\r\n"); } richTextBox1.AppendText("包含序號2=" + DictStudents.ContainsKey("序號2").ToString() + "\r\n"); richTextBox1.AppendText("包含序號9=" + DictStudents.ContainsKey("序號9").ToString() + "\r\n"); }
注意,1)Hashtable不僅需要強制轉換,由於它內部的特殊性,所得到的結果跟原序中的結果順序是不一致的,在需要排序的時候會很麻煩,但Dictionary<Tkey,Tvalue>不僅不需要強制轉換,而且順序跟原序是一致的。
2)Hashtable和Dictionary<Tkey,Tvalue>都仍具有.ContainsKey("序號2");(檢查是否包含某鍵值的項)的方法效率很高。
運行效果如圖:
三、SortedList與SortedList<Tkey,Tvalue>示例

SortedList sortListStudents = new SortedList(); SortedList<int, student> sortStudents = new SortedList<int, student>(); private void button2_Click(object sender, RoutedEventArgs e) { richTextBox1.Document.Blocks.Clear(); addData2(); showExemple2(); } private void addData2() { sortListStudents.Add(50, new student(1, "小顆豆一", true)); sortListStudents.Add(20, new student(3, "小顆豆二", false)); sortListStudents.Add(80, new student(5, "小顆豆三", true)); sortListStudents.Add(65, new student(2, "小顆豆四", false)); sortListStudents.Add(44, new student(4, "小顆豆五", true)); sortListStudents.Add(99, new student(6, "小顆豆六", false)); sortStudents.Add(50, new student(1, "小顆豆一", true)); sortStudents.Add(20, new student(3, "小顆豆二", false)); sortStudents.Add(80, new student(5, "小顆豆三", true)); sortStudents.Add(65, new student(2, "小顆豆四", false)); sortStudents.Add(44, new student(4, "小顆豆五", true)); sortStudents.Add(99, new student(6, "小顆豆六", false)); } private void showExemple2() { richTextBox1.AppendText("--------SortedList與SortedList<Tkey,Tvalue>示例--------\r\n"); richTextBox1.AppendText("--------非泛型數組的操作(需要強制轉換)---------\r\n"); foreach (DictionaryEntry item in sortListStudents ) { richTextBox1.AppendText(item.Key.ToString() + ((student)item.Value).ToString() + "\r\n"); } richTextBox1.AppendText("--------泛型數組的操作(不需要強制轉換)--------\r\n"); foreach (KeyValuePair <int,student > item in sortStudents ) { richTextBox1.AppendText(item.Key.ToString() + item.Value.ToString() + "\r\n"); } }
在這個例程中,兩個集合類工作都很好的實現了排序功能,差別仍是一個有強制轉換,一個不需要。運行效果如圖:
四、Queue與Queue<T>示例(先進先出)

Queue queueStuds = new Queue(); Queue <student > queueStudents=new Queue<student> ();//先進先出 private void button3_Click(object sender, RoutedEventArgs e) { richTextBox1.Document.Blocks.Clear(); addData3(); showExemple3(); } private void addData3() { queueStuds.Enqueue(new student(1, "小顆豆一", true)); queueStuds.Enqueue(new student(3, "小顆豆二", false)); queueStuds.Enqueue(new student(5, "小顆豆三", true)); queueStuds.Enqueue(new student(2, "小顆豆四", false)); queueStuds.Enqueue(new student(4, "小顆豆五", true)); queueStuds.Enqueue(new student(6, "小顆豆六", false)); queueStudents.Enqueue(new student(1, "小顆豆一", true)); queueStudents.Enqueue(new student(3, "小顆豆二", false)); queueStudents.Enqueue(new student(5, "小顆豆三", true)); queueStudents.Enqueue(new student(2, "小顆豆四", false)); queueStudents.Enqueue(new student(4, "小顆豆五", true)); queueStudents.Enqueue(new student(6, "小顆豆六", false)); } private void showExemple3() { richTextBox1.AppendText("--------Queue與Queue<T>示例(先進先出)--------\r\n"); richTextBox1.AppendText("--------非泛型數組的操作(需要強制轉換)---------\r\n"); while (queueStuds .Count >0) { richTextBox1.AppendText(((student)queueStuds.Dequeue()).ToString() + "\r\n"); } richTextBox1.AppendText("現在數組個數="+queueStuds.Count.ToString() + "\r\n"); richTextBox1.AppendText("--------泛型數組的操作(不需要強制轉換)(先進先出)---------\r\n"); while (queueStudents.Count > 0) { richTextBox1.AppendText(queueStudents.Dequeue().ToString() + "\r\n"); } richTextBox1.AppendText("現在數組個數=" + queueStudents.Count.ToString() + "\r\n"); }
Queue與Queue<T>都使用Enqueue()方法將一個對象加入到隊列中,按照先進先出的法則,入棧后使用Dequeue()方法出隊。出隊后該成員被移除,區別仍是強制轉換的問題。運行效果如圖:
五、Stack與Stack<T>示例(先進后出,注意顯示數據的順序)

Stack stackStudnets1 = new Stack(); Stack<student> stackStudents2 = new Stack<student>(); private void button4_Click(object sender, RoutedEventArgs e) { richTextBox1.Document.Blocks.Clear(); addData4(); showExemple4(); } private void addData4() { stackStudnets1.Push(new student(1, "小顆豆一", true)); stackStudnets1.Push(new student(3, "小顆豆二", false)); stackStudnets1.Push(new student(5, "小顆豆三", true)); stackStudnets1.Push(new student(2, "小顆豆四", false)); stackStudnets1.Push(new student(4, "小顆豆五", true)); stackStudnets1.Push(new student(6, "小顆豆六", false)); stackStudents2.Push(new student(1, "小顆豆一", true)); stackStudents2.Push(new student(3, "小顆豆二", false)); stackStudents2.Push(new student(5, "小顆豆三", true)); stackStudents2.Push(new student(2, "小顆豆四", false)); stackStudents2.Push(new student(4, "小顆豆五", true)); stackStudents2.Push(new student(6, "小顆豆六", false)); } private void showExemple4() { richTextBox1.AppendText("--------Stack與Stack<T>示例(先進后出,注意顯示數據的順序)--------\r\n"); richTextBox1.AppendText("--------非泛型數組的操作(需要強制轉換)---------\r\n"); for (int i = 0; i < stackStudnets1 .Count ; i++) { richTextBox1.AppendText(((student)stackStudnets1.Peek ()).ToString() + "\r\n"); } richTextBox1.AppendText("(Stack.Peek()是返回不移除)現在數組個數=" + stackStudnets1.Count.ToString() + "\r\n"); richTextBox1.AppendText("注意:以上數據次次返回的都是最后一次加到數組中的數----------" + "\r\n"); richTextBox1.AppendText("下邊:看看用Stack.Pop()返回並移除的結果:" + "\r\n"); while (stackStudnets1.Count > 0) { richTextBox1.AppendText(((student)stackStudnets1.Pop()).ToString() + "\r\n"); } richTextBox1.AppendText("(Stack..Pop()是返回並移除)現在數組個數=" + stackStudnets1.Count.ToString() + "\r\n"); richTextBox1.AppendText("--------泛型數組的操作(不需要強制轉換)---------\r\n"); while (stackStudents2.Count > 0) { richTextBox1 .AppendText ((stackStudents2 .Pop ().ToString ()+"\r\n")); } richTextBox1.AppendText("(Stack.Pop()是返回並移除)現在數組個數=" + stackStudents2.Count.ToString() + "\r\n"); }
這個集合類,本人感覺用在撤銷操作是最方便不過的了,專門記錄用戶的操作,當需要撤銷時,后進的是先出,對象所在位置都不需要判斷,如果是泛型直接用即可,如果是非泛型轉換一下。
注意,第一部分全是最后一次加入的成員,因為用的是Peek ()方法:返回不移除成員,永遠返回最后一個加入的成員;如果用.Pop()方法就不同了:返回並移除,顯示的效果及順序如下圖: