1.集合接口與集合類型 (1)集合的命名空間 大多數集合類都可以在System.Collections和System.Collections.Generic名稱空間中找到。泛型集合位於System.Collections.Generic名稱空間中;專用於特定類型的集合類位於System.Collections.Specialized名稱空間中;線程安全的集合位於System.Collections.Concurrent名稱空間中。 (2)集合接口介紹 1、IEnumerable與IEnumerator接口 其實IEnumerable接口是非常的簡單,只包含一個抽象的方法GetEnumerator(),它返回一個可用於循環訪問集合的IEnumerator對象。 public interface IEnumerable { IEnumerator GetEnumerator(); } IEnumerator對象有什麼呢?它是一個真正的集合訪問器,沒有它,就不能使用foreach語句遍歷集合或數組,因為只有IEnumerator對象才能訪問集合中的項。IEnumerator接口定義了:一個Current屬性用來獲取當前集合中的項;MoveNext方法將游標的內部位置向前移動;Reset方法將枚舉數設置為其初始位置,該位置位於集合中第一個元素之前。 public interface IEnumerator { object Current { get; } bool MoveNext(); void Reset(); } 一個collection要支持foreach進行遍歷,就必須實現IEnumerable,並以某種方式返回迭代器對象:IEnumerator。 2、集合和列表實現的接口表 接口 說明 IEnumerable<T> 如果foreach語句用於集合,就需要此接口。 ICollection<T> 此集合定義了Count屬性、CopyTo、Add、Remove、Clear方法 IList<T> 可以通過位置訪問幾何元素 ISet<T> 此集合不允許有重復的元素 IDictionary<K,V> 含有鍵值對的集合 ILookup<K,V> 含有鍵值對的集合,但可以通過一個鍵包含多個值 IComparer<T> 集合元素比較器,用於集合元素的排序 IEqualityComparer<T> 用於字典集合的比較器 IProducerConsumerCollection<T> 線程安全的集合 2.集合的基本操作 (1)創建集合 使用默認的構造函數創建一個空集合,元素添加到集合之後,集合的容量就會擴大為4。當集合的容量被使用完,且還在向集合中添加元素時,集合的容量就會擴大成原來的2倍!可使用Capacity屬性設置或訪問集合的容量,使用Count屬性訪問集合的元素個數。也可使用TrimExcess方法調整集合容量,節省內存! class Program { static void Main(string[] args) { List<int> list = new List<int>();//List<int> list = new List<int>(3) for (int i = 0; i < 1025; i++) { if (list.Count == (list.Capacity)) { Console.WriteLine("容量使用量:"+list.Count + "/" + list.Capacity); } list.Add(i); } Console.WriteLine("容量使用量:" + list.Count + "/" + list.Capacity); list.TrimExcess();//調整容量 Console.WriteLine("容量使用量:" + list.Count + "/" + list.Capacity); Console.Read(); } } 創建集合時可以為集合設置初始值,如下: List<int> list = new List<int>() { 1,2,3,4,5,6,7,8,9,0}; List<string> list = new List<int>() { "aa", "bb", "cc" }; (2)添加元素 為集合添加元素可使用Add方法,還可以使用AddRange方法一次添加多個元素,因為AddRange方法的參數是IEnumerable<T>類型的對象,所以可以添加數組到集合裡。 class Program { static void Main(string[] args) { List<string> list = new List<string>(); list.AddRange(new string[]{"aa","bb","cc"});//添加數組 list.AddRange(list);//添加集合 foreach (string s in list) { Console.WriteLine(s); } Console.Read(); } } (3)插入元素 插入元素可以使用Insert方法,同樣使用InsertRange方法插入多個元素,與AddRange方法類似。 class Program { static void Main(string[] args) { List<string> list = new List<string>() { "aa", "bb", "ff" }; list.Insert(2, "cc");//插入元素 list.InsertRange(3, new string[] { "dd", "ee" });//插入集合 foreach (string s in list) { Console.WriteLine(s); } Console.Read(); } } (4)訪問元素 可以使用索引器訪問集合中某個位置的元素,例如: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "aa", "bb", "cc" }; Console.WriteLine(list[1]);//訪問第1個位置上的元素,下標從0開始 Console.Read(); } } 集合的遍歷,除了使用foreach外,還可以使用集合的Foreach方法,該方法的參數是一個Action<T>委托類型,可以使用Lambda表達式。例如: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "aa", "bb", "cc" }; list.ForEach(temp => Console.WriteLine("元素:" + temp)); //也可使用: list.ForEach(Console.WriteLine); Console.Read(); } } (5)刪除元素 使用RemoveAt刪除指定位置的元素,使用Remove刪除指定的元素,使用RemoveRange刪除指定位置范圍的元素,使用Clear刪除所有元素,使用RemoveAll選擇刪除的元素。 注意:RemoveAll方法的參數是一個Predicate<T>委托類型,使用方式如下: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "12", "123", "1234", "12345" }; //刪除長度小於4的元素 list.RemoveAll(x => { bool flag = x.Length < 4; return flag; }); list.ForEach(Console.WriteLine); Console.Read(); } } (6)搜索元素 用於搜索的方法有:IndexOf、LastIndexOf、FindIndex、FindLastIndex、FindLast、Find、FindAll、Exists等方法。 IndexOf、LastIndexOf、FindIndex、FindLastIndex方法用於搜索元素的位置。 FindLast、Find、FindAll方法用於搜索滿足條件的元素。 Exists用於判斷集合是否存在某些元素。 參數可以使用Predicate<T>委托類型(Lambda表達式)的方法有:FindIndex、FindLastIndex、Find、FindLast、FindAll、Exists。例如: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "12", "123", "1234", "12345" }; //搜索長度小於4的元素 list=list.FindAll(x => { bool flag = x.Length < 4; return flag; }); list.ForEach(Console.WriteLine); Console.Read(); } } (7)元素排序 可以使用Sort方法對元素排序,也可以調用Reverse方法逆轉集合順序,Sort方法使用快速排序算法。Sort使用了幾個重載的方法,可以傳遞的參數有泛型委托Comparison<T>和泛型接口IComparer<T>,例如: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "12", "123", "1234", "12345" }; //按字符串長度排序 list.Sort((x, y) => { return y.Length - x.Length; }); list.ForEach(Console.WriteLine); Console.Read(); } } (8)類型轉換 使用ConvertAll<TOutput>方法可以把集合裡的所有元素轉換成另一種類型,ConvertAll<TOutput>使用了Converter委托,其定義如下: public sealeddelegate TOutput Converter<TInput, TOutput>(TInput from) TInput是委托方法的參數類型,TOutput是委托方法的返回值類型。例如: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "12", "123", "1234", "12345" }; //把字符串轉換成數字(String->int) List<int> array = list.ConvertAll<int>(temp => Int32.Parse(temp)); array.ForEach(temp => Console.WriteLine((temp + 1))); Console.Read(); } } (9)只讀集合 集合的AsReadOnly方法可以返回一個ReadOnlyCollection<T>類型的集合;ReadOnlyCollection<T>是一個只讀集合類型,除了不能使用修改集合的方法與屬性,其它與集合一樣。 (10)集合常用的擴展方法(常用) 集合的擴展方法很多,這裡只舉兩個例子:Select與Where。例如: Select:將序列中的每個元素投影到新表中。 class Program { static void Main(string[] args) { List<int> list = new List<int>() {1,3,5,7,9}; IEnumerable<string> array = list.Select<int, string>(x => (x * x).ToString()); foreach(string s in array) { Console.WriteLine(s); } Console.Read(); } } Where:基於謂詞篩選值序列。 class Program { static void Main(string[] args) { List<string> list = new List<string> { "1", "12", "123", "1234" }; //查詢長度小於3的元素 IEnumerable<string> query = list.Where(temp => temp.Length < 3); foreach (string s in query) { Console.WriteLine(s); } Console.Read(); } } 3.常見集合的特性 (1)非泛型集合對應的泛型集合 非泛型集合類 對應的泛型集合類 ArrayList List<T> HashTable DIctionary<T> Queue Queue<T> Stack Stack<T> SortedList SortedList<T> (2)隊列(Queue<T>) 隊列是其元素以先進先出的方式來處理的集合。該集合沒有Add與Remove方法,其重要的方法如下: Enqueue 在隊列的尾端添加元素 Dequeue 在隊列的頭部讀取並刪除元素 Peek 只讀取隊列頭部的元素 程序示例如: class Program { static void Main(string[] args) { Queue<string> queue = new Queue<string>(); for (int i = 1; i < 5; i++) { queue.Enqueue(i.ToString());//在尾部添加元素 } PrintQueue(queue);//輸出元素 string s = queue.Dequeue();//讀取並刪除頭部元素 Console.WriteLine("讀取並刪除頭部元素:" + s); queue.Enqueue("5");//在尾部添加元素 s = queue.Peek();//讀取頭部元素 Console.WriteLine("讀取頭部元素:" + s); PrintQueue(queue);//輸出元素 Console.Read(); } //輸出元素 private static void PrintQueue(Queue<string> q) { IEnumerable<string> list = q.Where(t => true); foreach (string t in list) { Console.WriteLine(t); } } } (3)棧(Stack<T>) 棧是一個後進先出的容器,其重要的方法如下: Push 在棧頂壓入一個元素入棧 Pop 從站頂讀取並刪除一個元素 Peek 只從站頂讀取一個元素 程序示例如: class Program { static void Main(string[] args) { Stack<string> stack = new Stack<string>(); for (int i = 1; i < 5; i++) { stack.Push(i.ToString());//入棧 } PrintStack(stack);//輸出元素 string s = stack.Pop();//讀取並刪除棧頂元素 Console.WriteLine("讀取並刪除棧頂元素:" + s); s = stack.Peek();//只讀取棧頂元素 Console.WriteLine("只讀取棧頂元素:" + s); PrintStack(stack);//輸出元素 Console.Read(); } //輸出元素 private static void PrintStack(Stack<string> s) { IEnumerable<string> list = s.Where(t => true); foreach (string t in list) { Console.WriteLine(t); } } } (4)鏈表(LinkedList<T>) LinkedList<T>是一個雙向鏈表。鏈表的優點是,如果將元素插入列表的中間位置,使用鏈表就會非常快,只需要修改下一個元素的Next引用與上一個元素的Previous引用。鏈表不能在集合中只存儲元素,必須要把元素存到LinkedListNode<T>類型的對象中,LinkedListNode<T>定義了屬性:List、Next、Value、Previous。List屬性返回與該節點相關的LinkedList<T>對象,Next、Previous用於遍歷鏈表。 LinkedList<T>對象可以訪問第一個和最後一個元素(Frist與Last)、在指定位置插入元素(AddAfter()、AddBefore()、AddFirst()、AddLast()方法),刪除指定位置的元素(ReMove()、ReMoveFirst()、RemoveLast()方法),從鏈表的開頭(Find())或結尾(FindLast())開始搜索元素。 程序示例如: class Program { static void Main(string[] args) { LinkedList<int> link = new LinkedList<int>(); LinkedListNode<int> node; for (int i = 0; i < 10; i++) { node = new LinkedListNode<int>(i); if (i % 2 == 0) { link.AddFirst(node);//從頭部添加節點 } else { link.AddLast(node);//從尾部添加節點 } } PrintLink(link);//輸出元素 Console.Read(); } //輸出元素 private static void PrintLink(LinkedList<int> l) { IEnumerable<int> list = l.Where(t => true); foreach (int t in list) { Console.WriteLine(t); } } } (5)有序列表(SortedList<K,V>) SortedList<K,V>是基於鍵對集合進行排序的集合類,它只允許每個鍵只有一個對應值,如果需要每個鍵對應多個值可以使用Lookup<K,V>。可以使用foreach遍歷該集合,枚舉器返回的是KeyValuePair<K,V>類型的元素。除了使用Add方法添加元素,還可以使用索引器添加。例如: class Program { static void Main(string[] args) { SortedList<string, string> sort = new SortedList<string, string>(); sort.Add("K1", "V1");//Add方法添加元素 sort["K2"] = "V2";//索引器添加元素 PrintSorted(sort);//輸出元素 Console.WriteLine("是否存在[K3]:" + sort.ContainsKey("K3")); string value = null; Console.WriteLine("是否存在[K2]:" + sort.TryGetValue("K2", out value)); Console.WriteLine("[K2]的值:" + value); Console.Read(); } //輸出元素 private static void PrintSorted(SortedList<string, string> s) { IEnumerable<KeyValuePair<string, string>> list = s.Where(t => true); foreach (KeyValuePair<string, string> t in list) { Console.WriteLine(t.Key + " ---> " + t.Value); } } } (6)字典 字典中鍵的類型必須重寫Object類的GetHashCode()方法。只要字典類需要確定元素的位置,他就會調用GetHashCode()方法。除了實現GetHashCode()方法外,鍵類型還必須實現IEquatable<T>.Equals()方法,或重寫Object類的Equals()方法。 因為不同的對象可能返回相同的散列碼(GetHashCode方法返回值),但是如果A.Equals(B)返回true,則A和B的散列碼就必須相同。這似乎有點奇怪,但這非常重要,因為字典的性能取決於GetHashCode()方法實現的代碼。 1、Dictionary<K,V> Dictionary<K,V>類支持每個鍵關聯一個值,與SortedList<K,V>很類似。 2、Lookup<K,V> Lookup<K,V>類把鍵映射到一個值集上。創建Lookup<K,V>類對象必須調用ToLookup()擴展方法,該方法返回一個Lookup<K,V>對象。ToLookup()方法需要一個Func<T,K>類型的委托參數,Func<T,K>類型定義了鍵的選擇器。 ToLookup()是一個奇妙的函數,用於對一個集合進行操作,創建一個1:n 的映射。 它可以方便的將數據分類成組,並生成一個字典供查詢使用。例如: class Program { static void Main(string[] args) { List<string> list = new List<string>() { "1","12","123","1234","a","ab","abc"}; var lookup=list.ToLookup(x => x.Length);//根據元素的長度分組 foreach (var t in lookup) { List<string> temp=t.ToList<string>();//把一組數據轉換成集合 temp.ForEach(x=>Console.Write(x+"、"));//輸出集合 Console.WriteLine(); } Console.Read(); } } 3、SortedDictionary<K,V> SortedDictionary<K,V>類是一個二叉搜索樹,其中的元素根據鍵來排序,該鍵類型必須實現IComparable<T>接口。 SortedDictionary<K,V>與SortedList<K,V>的區別: SortedList<K,V>類使用的內存比SortedDictionary<K,V>少。 SortedDictionary<K,V>類的元素的插入與刪除比較快。 在用於已排好序的集合,若不需要修改容量SortedList<K,V>會比較快。 區別的根本原因:SortedList<K,V>實現的是一個基於數組的集合,SortedDictionary<K,V>實現的是一個基於字典的集合。 (7)集(ISet<T>接口) 不包含重復元素的集合稱為集,.NET Framework包含兩個集(HashSet<T>和SortedSet<T>),它們都實現ISet<T>接口。HashSet<T>是不包含重復元素的無序列表,SortedSet<T>是不包含重復元素的有序列表。 4.可觀察的集合(深入) (1)ObservableCollection<T>類簡介 ObservableCollection<T>類是為WPF定義的,這樣UI就可以得知集合的變化,因為只要修改了ObservableCollection<T>類對象集合中的元素,它就會產生CollectionChanged 事件。 (2)使用示例 class Program { static void Main(string[] args) { ObservableCollection<int> list = new ObservableCollection<int>(); list.CollectionChanged += ListCollectionChanged;//添加事件方法 list.Add(10);//產生事件 list.Add(20);//產生事件 list.Remove(10);//產生事件 Console.Read(); } //事件方法 private static void ListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { Console.WriteLine("本次修改的方式:" + e.Action.ToString()); if (e.OldItems != null) { Console.WriteLine("本次修改的位置:" + e.OldStartingIndex); Console.WriteLine("本次修改的元素個數:" + e.OldItems.Count); } if (e.NewItems != null) { Console.WriteLine("本次修改的位置:" + e.NewStartingIndex); Console.WriteLine("本次修改的元素個數:" + e.NewItems.Count); } Console.WriteLine(); } } 5.位數組(深入) (1)BitArray、BitVector32介紹 如果需要處理的數字有許多位,就可以使用BitArray類與BitVector32結構。它們的區別是:BitArray類可以重新設置大小,它可以包含很多位;BitVector32結構是基於棧的,因此比較快,但它只能包含32為,它們存在一個整數中。 (2)BitArray BitArray類是一個引用類型,它包含一個int數組,其中32位使用一個新整數,其成員如下: Count、Length Count、Length可得到數組的位數,Length可設置其數組的大小 Get、Set 使用Get、Set可訪問數組中的位 SetAll SetAll()方法設置所有的位值 Not Not()方法對數組中所有的位求反 And、Or、Xor And()是與操作,Or()是或操作,Xor()是異或操作 程序示例: class Program { static void Main(string[] args) { BitArray barray = new BitArray(20); barray.SetAll(true);//對所有位賦值 PrintBitArray(barray);//輸出 barray.Set(1, false); barray[2] = false; PrintBitArray(barray);//輸出 Console.Read(); } //輸出函數 private static void PrintBitArray(BitArray b) { foreach (bool bit in b) { Console.Write(bit ? 1 : 0); } Console.WriteLine(); } } (3)BitVector32 BitVector32結構的效率更高,因為它是值類型!它常用成員如下: Data Data屬性把BitVector32結構返回成一個整數 索引器 訪問BitVector32結構的值可以使用索引器 CreateMask 這是一個靜態方法,為訪問BitVector32結構中特定的類創建掩碼 CreateSection 這是一個靜態方法,用於創建32位中的幾個片段 程序示例: class Program { static void Main(string[] args) { BitVector32 b32 = new BitVector32(); PrintBitVector32(b32);//輸出位 int bit = BitVector32.CreateMask(); for (int i = 0; i < 4; i++) { b32[bit] = true; Console.WriteLine(bit); PrintBitVector32(b32);//輸出位 b32[bit] = false; bit = BitVector32.CreateMask(bit); } Console.WriteLine(b32.Data);//輸出數 Console.Read(); } //輸出函數 private static void PrintBitVector32(BitVector32 b32) { for (int i = 0; i < 32; i++) { Console.Write(b32[i] ? 1 : 0); } Console.WriteLine(); } } 6.並發集合(.NET 4新增) (1)ConcurrentQueue<T> 這個集合類用一種免鎖定的算法實現,訪問隊列元素的方法有:TryDequeue、TryPeek和Enqueue。 (2)ConcurrentStack<T> 這個集合類定義了:Push()、PushRange、TryPeek、TryPop、TryPopRange方法。 (3)ConcurrentBag<T> ConcurrentBag<T>表示對象的線程安全的無序集合。對於同一個線程值的添加和刪除是非常快的,因為ConcurrentBag內部將數據按線程的標識而獨立存儲,所以一個線程從自己的數據中移除一個數據是非常快的,當然如果這個線程中沒有數據,那麼只能從其他線程中移除數據,此時會發生一些性能損耗從而確保線程安全! (4)ConcurrentDictionary<K, V> 這是一個線程安全的鍵值集合,TyrAdd、TryGetValue、TryRemove、TryUpdate方法以非阻塞的方式訪問元素。 (5)BlockingCollection<T> 這個集合在可以添加或提取元素元素之前,會阻塞線程並一直等待。可以使用Add、Take方法用來添加和刪除元素,這些方法會阻塞當前線程,一直等到任務可以執行為止。 7.不同集合類的性能