日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#集合通论

發(fā)布時(shí)間:2025/3/15 C# 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#集合通论 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

  寫這篇文章的最初動(dòng)力是來自于一次筆試經(jīng)歷。有一道筆試題大概是這樣的:程序使用一個(gè)txt文件來存儲(chǔ)操作記錄。存儲(chǔ)記錄是多行字符串,每一行代表一次操作記錄,格式如下:用戶名+操作事項(xiàng)名稱+操作時(shí)間。現(xiàn)在假設(shè)這個(gè)txt文件已經(jīng)非常大了,要求對(duì)這個(gè)文件做一些處理(具體記不太清了,接近于一些邏輯處理和增刪改)。毫無疑問,對(duì)于txt文件來說,要對(duì)之中的數(shù)據(jù)進(jìn)行處理,首先要把數(shù)據(jù)讀入內(nèi)存,這就涉及到選擇何種數(shù)據(jù)結(jié)構(gòu)的問題了。基于自己的常規(guī)思維,我不加思索就選擇了自定義類的List泛型存儲(chǔ)數(shù)據(jù)。之后再與面試官交流的時(shí)候,他給出了用Dictionary泛型的解決方案。由于自己的認(rèn)知局限,當(dāng)時(shí)沒聽明白面試官的具體解釋,導(dǎo)致這道問題的討論就成了單方面的闡述,失去了雙方的交流。面試也是發(fā)現(xiàn)問題的一種途徑。對(duì)于集合知識(shí),自身確實(shí)存在認(rèn)知匱乏的問題,而在許多程序中,選擇合適的數(shù)據(jù)結(jié)構(gòu)往往是決定整個(gè)算法或代碼是否簡(jiǎn)潔優(yōu)雅的關(guān)鍵所在,如果對(duì)集合都不熟悉的話,那么談何選擇合適的數(shù)據(jù)結(jié)構(gòu)呢?如果你也存在和我一樣的問題,可以嘗試著繼續(xù)讀這篇文章,或許能給你帶來幫助。

  這篇文章討論的主題是集合,重點(diǎn)在于分析集合的區(qū)別和聯(lián)系,加深對(duì)集合的認(rèn)知與使用,熟悉常用C#類庫(kù)有關(guān)集合類的組織結(jié)構(gòu)。

數(shù)組

  論集合,不得不談的第一項(xiàng)就是數(shù)組。C#數(shù)組需要聲明元素類型,元素類型可以是值類型也可以是引用類型,數(shù)組是靜態(tài)類型,初始化必須指定個(gè)數(shù)大小,且創(chuàng)建后的數(shù)組是連續(xù)存放在內(nèi)存中的。聲明方式如下所示:

int[] intArray = new int[10];      //整型數(shù)組 string[] stringArray = new string[10]; //字符串?dāng)?shù)組 Random[] randArray = new Random[10]; //類數(shù)組

  數(shù)組是從Array隱式派生的,這是由編譯器完成的,Array類被組織在System命名空間下。

Array myArray1 = new int[10]; Array myArray2 = new string[10]; Array myArray3 = new Random[10];

?數(shù)組是引用類型,初始化后分配在堆上。

System.Collections

  1. System.Collections 組織空間

? ? ??

?集合類型根據(jù)自身的需求實(shí)現(xiàn)了左邊接口中的一個(gè)或多個(gè),按照實(shí)現(xiàn)接口大概可以分為三種:有序集合(ICollection),索引集合(IList),鍵式集合(IDictionary)。

  2. 通用接口

public interface IEnumerator {bool MoveNext();object Current { get; }void Reset(); }public interface IEnumerable { IEnumerator GetEnumerator(); }public interface ICollection : IEnumerable {int Count { get; }bool IsSynchronized { get; }object SyncRoot { get; }void CopyTo(Array array, int index); }public interface IList : ICollection, IEnumerable {bool IsFixedSize { get; }bool IsReadOnly { get; }object this[int index] { get; set; }int Add(object value);void Clear();bool Contains(object value);int IndexOf(object value);void Insert(int index, object value);void Remove(object value);void RemoveAt(int index); }public interface IDictionary : ICollection, IEnumerable {bool IsFixedSize { get; }bool IsReadOnly { get; }object this[object key] { get; set; }ICollection Keys { get; }ICollection Values { get; }void Add(object key, object value);void Clear();bool Contains(object key);IDictionaryEnumerator GetEnumerator();void Remove(object key); } Collections常用接口實(shí)現(xiàn)

?  集合類型都實(shí)現(xiàn)了IEnumerable接口,從而可以使用foreach迭代。

   實(shí)現(xiàn)了ICollectoin接口的集合類表明集合中的元素是有先后順序的。

   IList接口繼承了ICollection接口,實(shí)現(xiàn)了IList接口的集合類不止表明集合中的元素是有先后順序,而且表明集合類可以通過下標(biāo)訪問的方式訪問集合元素。

   IDictionary接口也繼承了ICollection接口,實(shí)現(xiàn)了IDicionary接口的集合類可以通過下標(biāo)key訪問的訪問方式訪問集合元素。

  3. ArrayList

  Array是靜態(tài)分配的,意味著創(chuàng)建數(shù)組后的大小不能改變,然而實(shí)際應(yīng)用中,我們很多時(shí)候無法在一開始確定數(shù)組的大小,這樣便需要一種能動(dòng)態(tài)分配的數(shù)組類型,ArrayList就是為此而生的。ArrayList主要實(shí)現(xiàn)的接口有IList,所以這是一個(gè)索引集合。

//聲明ArrayList ArrayList myArray = new ArrayList();//插入元素 可以為值類型 也可以為引用類型 myArray.Add(1); myArray.Add("hello"); myArray.Add(new Random());int i = (int)myArray[0]; string str = (string)myArray[1]; Random rand = (Random)myArray[2];

  使用Reflector查看ArrayList的底層實(shí)現(xiàn),可以看到動(dòng)態(tài)數(shù)組其實(shí)是由一個(gè)object[] _items維系著的,這就解釋了為什么集合的元素可以為值類型也可以為引用類型。這樣的底層實(shí)現(xiàn)貌似靈活性很高,集合可以容納異構(gòu)類型,然而卻帶來了性能上的問題,因?yàn)楫?dāng)插入值類型的時(shí)候,就存在隱式裝箱操作,而當(dāng)把元素還原為值類型變量的時(shí)候,又發(fā)生了一次顯式拆箱操作,如果這種裝箱拆箱存在上千萬次,那么程序的性能是要大打折扣的。同時(shí)要注意一點(diǎn)的是object類型可以強(qiáng)制轉(zhuǎn)化為任何類型,這是說編譯器不會(huì)檢查object強(qiáng)制轉(zhuǎn)換的類型,如果無法轉(zhuǎn)換的話,這必須等到運(yùn)行時(shí)才能確定出錯(cuò),這就是類型安全問題。

  所以應(yīng)該盡量避免使用ArrayList。

  4. Stack 和 Queue

  對(duì)于棧和隊(duì)列這兩種經(jīng)典的數(shù)據(jù)結(jié)構(gòu),C#也將它們組織在System.Collections命名空間中。Stack是后進(jìn)先出的結(jié)構(gòu),Queue是先進(jìn)先出的結(jié)構(gòu),這兩者主要實(shí)現(xiàn)的接口有ICollection,表示它們都是有序集合,應(yīng)注意到這兩者都不可以使用下標(biāo)訪問集合元素。這兩者的底層實(shí)現(xiàn)都是由一個(gè)object[] _array維系著,都存在著裝箱拆箱的性能問題和類型安全問題,所以應(yīng)該盡量避免直接使用它們。

  5. HashTable

?  ?前面我們提到的集合類都屬于單元素集合類,實(shí)際應(yīng)用中我們需要一種鍵值對(duì)的形式存儲(chǔ)數(shù)據(jù),即集合類中存儲(chǔ)的不再是單個(gè)元素,而是key-value兩個(gè)元素,HashTable就是為此而生的。HashTable實(shí)現(xiàn)了IDictionary接口。

//創(chuàng)建一個(gè)HashTable實(shí)例 Hashtable hashDict = new Hashtable();//往容器中加入key-value hashDict.Add("a", "hello"); hashDict.Add("b", "hello"); hashDict.Add("c", "go"); hashDict.Add(4, 300);//通過下標(biāo)key獲取value string str1 = hashDict["a"].ToString(); string str2 = (string)hashDict["b"]; int i4 = (int)hashDict[4];

?  HashTable中的key關(guān)鍵字必須唯一,不能重復(fù)。如果深入到HashTable的底層實(shí)現(xiàn),應(yīng)該可以清楚的看到key和value是結(jié)構(gòu)體bucket數(shù)組維護(hù)著,bucket中key和value的實(shí)現(xiàn)也是object,所以存在著與ArrayList,Stack,Queue同樣的問題,應(yīng)該盡量避免使用HashTable。

?為什么鍵值集合類要命名為HashTable?

? ? ?從HashTable的命名來看,我們可以斷定說鍵值集合跟Hash必定存在某種聯(lián)系。哈希又稱為散列,散列技術(shù)是在記錄的存儲(chǔ)位置和它的關(guān)鍵字之間建立一個(gè)確定的對(duì)應(yīng)關(guān)系f,使得每個(gè)關(guān)鍵字key對(duì)應(yīng)一個(gè)存儲(chǔ)位置f(key),f又稱為散列函數(shù)。HashTable實(shí)現(xiàn)了IDictionary接口,表明HashTable可以通過下標(biāo)key訪問的方式獲取數(shù)組元素,即 ? ? HashTable[key],這與實(shí)現(xiàn)了IList接口的集合類的數(shù)字下標(biāo)訪問存在著明顯的不同。那么如何通過key快速定位得到value呢?我相信通過前面的鋪墊大伙都知道是什么回事了,對(duì),就是哈希函數(shù)的運(yùn)用。具體可以參考文章http://www.cnblogs.com/abatei/archive/2009/06/23/1509790.html

  6. SortedList

  在控制臺(tái)應(yīng)用程序下運(yùn)行以下代碼并觀察結(jié)果:

Hashtable hashDict = new Hashtable(); hashDict.Add("key1", "1"); hashDict.Add("key2", "2"); hashDict.Add("key3", "3"); hashDict.Add("key4", "4");foreach (string key in hashDict.Keys) {Console.WriteLine(hashDict[key]); }

? ? ? 我們可以看到這里并沒有按照預(yù)期的Key順序輸出Value。也就是說HashTable是不按照key順序排序的,具體原理可以參考HashTable的源碼。SortedList很好地解決了鍵值集合順序輸出的問題。

//創(chuàng)建一個(gè)SortedList實(shí)例 SortedList sortList = new SortedList();//往容器中加入元素 sortList.Add("key1", 1); sortList.Add("key2", 2); sortList.Add("key3", 3); sortList.Add("key4", 4);//獲取按照key排序的第index個(gè)Key string str1 = sortList.GetKey(0).ToString(); Console.WriteLine(str1); //獲取按照key排序的第index個(gè)Value int i1 = (int)sortList.GetByIndex(0); Console.WriteLine(i1.ToString()); //下標(biāo)key訪問 string str2 = sortList["key2"].ToString(); Console.WriteLine(str2);//遍歷sortList foreach (DictionaryEntry item in sortList) {Console.WriteLine(item.Key);Console.WriteLine(item.Value); }

?  SortedList實(shí)現(xiàn)了IDictionary接口,所以可以使用下標(biāo)key訪問元素的形式,同時(shí)要求key必須唯一。

?我們知道HashTable通過使用哈希函數(shù)通過key快速找到存儲(chǔ)位置,那么SortedList又是如何實(shí)現(xiàn)下標(biāo)key訪問元素?

  SortedList的底層實(shí)現(xiàn)與HashTable有著本質(zhì)的區(qū)別。SortedList中使用object[] keys 和 object[] values 兩個(gè)對(duì)象數(shù)組分別來存儲(chǔ)key和value,要求實(shí)現(xiàn)能按照key有序輸出,在下標(biāo)key訪問的時(shí)候就無法使用Hash函數(shù)了,所以SortedList雖然也是鍵值集合,但與Hash卻沒有任何聯(lián)系。通過查看SortedList的底層代碼,原來它的實(shí)現(xiàn)是二分查找(BinarySearch),也就是說要求key是有序排列的,在查找的時(shí)候進(jìn)行二分比較搜索,找到對(duì)應(yīng)的index,從而返回values[index]。

?那么如何在HashTable和SortedList中做出選擇?

  如果需要實(shí)現(xiàn)按照key有序輸出,那么毫無疑問就要選擇SortedList了。如果不需要按照key有序輸出,在小數(shù)據(jù)量的情況下,兩者選擇任何一個(gè)性能都應(yīng)該差不多,但大數(shù)據(jù)量的情況下,則更應(yīng)該選擇HashTable。為什么呢?理由有兩點(diǎn)。1.HashTable的key下標(biāo)訪問更直接更快。通過上面分析我們知道SortedList的key下標(biāo)訪問是由二分查找實(shí)現(xiàn)的,實(shí)現(xiàn)的時(shí)間復(fù)雜度為O(log n),而Hash函數(shù)的時(shí)間復(fù)雜度為O(1),HashTable的實(shí)現(xiàn)更優(yōu)。2.SortedList要求key有序,這意味著在插入的時(shí)候必須適當(dāng)?shù)匾苿?dòng)數(shù)組,從而達(dá)到有序的目的,所以存在性能上的消耗,HashTable的實(shí)現(xiàn)更優(yōu)。

  ?SortedList也并沒有走出裝箱拆箱性能和類型安全的圈子,所以應(yīng)該盡量避免直接使用它。

System.Collections.Generic

  1. System.Collections.Generic 組織空間

  

System.Collections.Generic是.NET 2.0新增的一個(gè)命名空間。C#1中的集合類型都存在著裝箱拆箱的性能問題以及潛在的類型安全問題,丑陋的設(shè)計(jì)必須得到改進(jìn),于是C#2就引入了泛型這個(gè)概念。

  2. 何為集合泛型

  新的命名空間下,可以看到接口或集合類后都攜帶了<T>或<TKey,TValue>。很明顯,攜帶<T>對(duì)應(yīng)的是單元素集合,攜帶<TKey,TValue>對(duì)應(yīng)的是鍵值集合。那么泛型又是如何工作的呢?來看一下其編譯過程吧:初次編譯時(shí),首先生成IL代碼和元數(shù)據(jù),T(TKey,TValue)只是作為類型占位符,不進(jìn)行泛型類型的實(shí)例化;在進(jìn)行JIT編譯時(shí),將以實(shí)際類型替換IL代碼和元數(shù)據(jù)中的T占位符,并將其轉(zhuǎn)換為本地代碼,下一次對(duì)該泛型類型的引用將使用相同的本地代碼。

  泛型即解決了裝箱拆箱的性能問題,又解決了潛在的類型轉(zhuǎn)換安全問題,所以在實(shí)際應(yīng)用中,推薦使用泛型集合代替非泛型集合。

  3. 泛型集合與非泛型集合的對(duì)應(yīng)?

  ArrayList => List<T> ?新的泛型集合去掉了Array前綴。

  Stack,Queue => Stack<T>,Queue<T>

  HashTable => Dictionary<TKey,TValue> 新的泛型鍵值集合的命名放棄了HashTable,但其內(nèi)部實(shí)現(xiàn)原理還是和HashTable有很大相似的。

  SortedList => SortedList<TKey,TValue> | SortedDictionary<TKey,TValue>

?如何區(qū)分SortedList<TKey,TValue> 和?SortedDictionary<TKey,TValue>?

  ?SortedList<TKey,TValue>的底層實(shí)現(xiàn)基本是按照SortedList,下標(biāo)key訪問是二分查找的O(log n),插入和移除運(yùn)算復(fù)雜度是O(n)。而SortedDictionary<TKey,TValue> 底層實(shí)現(xiàn)是二叉搜索樹,下標(biāo)key訪問也為O(log n),插入和移除運(yùn)算復(fù)雜度是O(log n)。所以SortedList<TKey,TValue>使用的內(nèi)存會(huì)比SortedDictionary<TKey,TValue>小,SortedDictionary<TKey,TValue>在插入和移除元素的時(shí)候更快。

轉(zhuǎn)載于:https://www.cnblogs.com/teroy/p/3134666.html

總結(jié)

以上是生活随笔為你收集整理的C#集合通论的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。