c#集合解析

什麼是集合(collection)?面試

提供了一種結構化組織任意對象的方式,從.NET 的角度看,所謂的集合能夠定義爲一種對象,這種對象實現一個或者多個System.Collections.ICollection、 System.Collections.IDictionary和System.Collections.IList接口。這必定義把 System.Collections名稱空間中的「內置」集合劃分紅了三種類別:算法

  *  有序集合:僅僅實現ICollection接口的集合,在一般狀況下,其數據項目的插入順序控制着從集合中取出對象的的順序。 System.Collections.Stack和 System.Collections.Queue類都是ICollection集合的典型例子。
    *  索引集合:實現Ilist的集合,其內容能經由從零開始的數字檢索取出,就象數組同樣。System.Collections.ArrayList對象是索引集合的一個例子。
    *  鍵式集合:實現 IDictionary 接口的集合,其中包含了能被某些類型的鍵值檢索的項目。IDictionary集合的內容一般按鍵值方式存儲,能夠用枚舉的方式排序檢索。 System.Collections.HashTable類實現了IDictionary 接口。數據庫

以OOP (面向對象編程)的術語來講,上面以接口這種方式構造對象的功能,就是多態技術。編程

System.Collections 命名空間包含接口和類,這些接口和類定義各類對象(如列表、隊列、位數組、哈希表和字典)的集合。
System.Collections.Generic 命名空間包含定義泛型集合的接口和類,泛型集合容許用戶建立強類型集合,它能提供比非泛型強類型集合更好的類型安全性和性能。
System.Collections.Specialized 命名空間包含專用的和強類型的集合,例如,連接的列表詞典、位向量以及只包含字符串的集合。數組

在System.Collections命名空間中提供了許多接口:安全

      • IEnumerable循環集合項目
      • ICollection能夠獲取集合中項目個數
      • IList項目列表
      • IDictionary提供了鍵碼索引

 


  • ArrayList
  • SortedList
  • Queue,Stack
  • HashTable
  • NameValueCollection
  • SortedList
  • 泛型集合

什麼是ArrayList?多線程

  一種正常數組和集合的混合類型(動態數組,就是Array的複雜版本)ide

優勢:動態的增長和減小元素函數

    實現了ICollection和IList接口性能

    靈活的設置數組的大小

缺點:ArrayList負載比傳統數組更大並且沒有實現嚴格的類型化,也就能夠接受任何轉換爲System.Object的對象,存在類型安全和效率問題(面試點)

如何使用ArrayList 

 1 ArrayList al = new ArrayList();
 2  al.Add(100);//單個添加
 3  foreach (int number in new int[6] { 9, 3, 7, 2, 4, 8 })
 4  {
 5      al.Add(number);//集體添加方法一
 6  }
 7  al.AddRange(new int[2] { 11, 12 });//集體添加方法二
 8  ArrayList al2 = new ArrayList(al.GetRange(1, 3));//新ArrayList只取一部份
 9  foreach (int i in al)//不要強制轉換
10  {
11      Console.WriteLine(i);
12  }
13  for (int i = 0; i < al2.Count; i++)//數組是length
14  {
15      int number = (int)al2[i];//必定要強制轉換
16      Console.WriteLine(number);
17  }
18  IEnumerator ie=al.GetEnumerator();
19  while(ie.MoveNext())
20  {
21       Console.Write(ie.Curret.ToString()+" ");
22  }
23  Int32[] values = (Int32[])al.ToArray(typeof(Int32));//返回ArrayList包含的數組
24 View Code
View Code

ArrayList重要的方法和屬性

 1 public ArrayList()
 2     {
 3         this._items = emptyArray;
 4     }
 5     public ArrayList(ICollection c)
 6     {
 7         if (c == null)
 8         {
 9             throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
10         }
11         int count = c.Count;
12         if (count == 0)
13         {
14             this._items = emptyArray;
15         }
16         else
17         {
18             this._items = new object[count];
19             this.AddRange(c);
20         }
21     }
22     public ArrayList(int capacity)
23     {
24         if (capacity < 0)
25         {
26             throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", new object[] { "capacity" }));
27         }
28         if (capacity == 0)
29         {
30             this._items = emptyArray;
31         }
32         else
33         {
34             this._items = new object[capacity];
35         }
36     } public override int Add(object obj)
37         {
38             int num = this._list.Add(obj);
39             base._version++;
40             return num;
41         }
View Code

 IsSynchronized屬性,ArrayList.Synchronized方法
1-》IsSynchronized屬性指示當前的ArrayList實例是否支持線程同步 2-》而ArrayList.Synchronized靜態方法則會返回一個ArrayList的線程同步的封裝
1-》若是使用非線程同步的實例,那麼在多線程訪問的時候,須要本身手動調用lock來保持線程同步,例如:
ArrayList list = new ArrayList();
//...
lock( list.SyncRoot ) //當ArrayList爲非線程包裝的時候,SyncRoot屬性其實就是它本身,可是爲了知足ICollection的SyncRoot定義,這裏仍是使用SyncRoot來保持源代碼的規範性
{
list.Add( 「Add a Item」 );
}
2-》若是使用ArrayList.Synchronized方法返回的實例,那麼就不用考慮線程同步的問題,這個實例自己就是線程安全的,實際上ArrayList內部實現了一個保證線程同步的內部類,ArrayList.Synchronized返回的就是這個類的實例,它裏面的每一個屬性都是用了lock關鍵字來保證線程同步。

可是,使用這個方法(ArrayList.Synchronized)並不能保證枚舉的同步,例如,一個線程正在刪除或添加集合項,而另外一個線程同時進行枚舉,這時枚舉將會拋出異常。因此,在枚舉的時候,你必須明確使用 SyncRoot 鎖定這個集合。

 ArrayList與數組轉換

 1 ArrayList List = new ArrayList();
 2 List.Add(1);
 3 List.Add(2);
 4 List.Add(3);
 5 Int32[] values = (Int32[])List.ToArray(typeof(Int32));
 6 
 7 ArrayList List = new ArrayList();
 8 List.Add(1);
 9 List.Add(2);
10 List.Add(3);
11 Int32[] values = new Int32[List.Count];
12 List.CopyTo(values);
13 
14 
15 //往數組中添加不一樣類型的元素
16 ArrayList List = new ArrayList();
17 List.Add( 「string」 );
18 List.Add( 1 );
19 object[] values = List.ToArray(typeof(object)); //正確
20 string[] values = (string[])List.ToArray(typeof(string)); //錯誤
View Code

ArrayList最佳使用建議
    這一節咱們來討論ArrayList與數組的差異,以及ArrayList的效率問題
  (1)ArrayList是Array的複雜版本,ArrayList內部封裝了一個Object類型的數組,從通常的意義來講,它和數組沒有本質的差異,甚至於ArrayList的許多方法,如Index、IndexOf、Contains、Sort等都是在內部數組的基礎上直接調用Array的對應方法。
  (2)內部的Object類型的影響對於通常的引用類型來講,這部分的影響不是很大,可是對於值類型來講,往ArrayList裏面添加和修改元素,都會引發裝箱和拆箱的操做,頻繁的操做可能會影響一部分效率。消除這個影響是沒有辦法的,除非你不用它,不然就要承擔一部分的效率損失,不過這部分的損失不會很大。
  (3)數組擴容,這是對ArrayList效率影響比較大的一個因素。每當執行Add、AddRange、Insert、InsertRange等添加元素的方法,都會檢查內部數組的容量是否不夠了,若是是,它就會以當前容量的兩倍來從新構建一個數組,將舊元素Copy到新數組中,而後丟棄舊數組,在這個臨界點的擴容操做,應該來講是比較影響效率的。
     例1:好比,一個可能有200個元素的數據動態添加到一個以默認16個元素大小建立的ArrayList中,將會通過:
  16*2*2*2*2 = 256四次的擴容纔會知足最終的要求,那麼若是一開始就以:ArrayList List = new ArrayList( 210 );的方式建立ArrayList,不只會減小4次數組建立和Copy的操做,還會減小內存使用。
(4)頻繁的調用IndexOf、Contains等方法(Sort、BinarySearch等方法通過優化,不在此列)引發的效率損失
首先,咱們要明確一點,ArrayList是動態數組,它不包括經過Key或者Value快速訪問的算法,因此實際上調用IndexOf、Contains等方法是執行的簡單的循環來查找元素,因此頻繁的調用此類方法並不比你本身寫循環而且稍做優化來的快,若是有這方面的要求,建議使用Hashtable或SortedList等鍵值對的集合。


 隊列(Queue)棧(Stack):

  System.Collections.Stack 和 System.Collections.Queue 類,二者僅僅實現了ICollection 接口,按照存儲項目加到集合的順序保存System.Object類型的項目。對象只能按其加入順序從集合中檢索:堆棧是後進先出,而隊列則是先進先出。一般狀況下,你在如下場合能夠考慮採用以上這些集合:

    *   接收和處理集合內項目時順序比較重要。
    *   你能在處理項目以後丟棄它。
    *   你不須要訪問集合中的任意項目。

 1 隊列
 2 Queue qu = new Queue();
 3 Queue qu2 = new Queue();
 4 foreach (int i in new int[4] { 1, 2, 3, 4 })
 5 {
 6     qu.Enqueue(i);//入隊
 7     qu2.Enqueue(i);
 8 }
 9 
10 foreach (int i in qu)
11 {
12     Console.WriteLine(i);//遍歷
13 }
14 
15 qu.Dequeue();//出隊
16 Console.WriteLine("Dequeue");
17 foreach (int i in qu)
18 {
19     Console.WriteLine(i);
20 }
21 
22 qu2.Peek();//返回位於 Queue 開始處的對象但不將其移除。
23 Console.WriteLine("Peek");
24 foreach (int i in qu2)
25 {
26     Console.WriteLine(i);
27 }
28 
29 30 Stack sk = new Stack();
31 Stack sk2 = new Stack();
32 foreach (int i in new int[4] { 1, 2, 3, 4 })
33 {
34     sk.Push(i);//入棧
35     sk2.Push(i);
36 }
37 
38 foreach (int i in sk)
39 {
40     Console.WriteLine(i);//遍歷
41 }
42 
43 sk.Pop();//出棧
44 Console.WriteLine("Pop");
45 foreach (int i in sk)
46 {
47     Console.WriteLine(i);
48 }
49 
50 sk2.Peek();//彈出最後一項不刪除
51 Console.WriteLine("Peek");
52 foreach (int i in sk2)
53 {
54     Console.WriteLine(i);
55 }
View Code

HashTable:

  System.Collections.HashTable集合實現了IDictionary 和 Icollection,能用來存儲多種類型的對象連同關聯的惟一字符串鍵值。在HashTable集合中的項目按照源自其鍵值的哈希代碼所肯定的順序存儲。集合內每一個對象的鍵值都必須惟一,而其哈希代碼則不必定惟一。
什麼是哈希代碼?哈希代碼實質上就是從一塊數據中消除全部冗餘部分以後的結果,它主要起到對數據輔助分類或排序的做用。當某個項目加入集合時,HashTable即調用鍵值的GetHashCode方法,因爲全部的類都是從 System.Objec繼承的,因此調用該方法便可肯定該類的哈希代碼而且按該代碼排序存儲。你能夠強迫使用定製的哈希函數,方法有二,一是重載類的 GetHashCode方法,二是向 HashTable構造器傳遞實現了System.Collections.IHashcodeProvider接口的對象,在這種狀況下,該對象將用於爲全部加入集合的鍵值產生哈希代碼。

從性能的角度看,由於鍵值搜索僅限於具備一樣哈希代碼的鍵值,因此HashTable可以很快地從集合中檢索任意一個元素,從而減小了必須經過檢查以發現匹配的鍵值的數量。然而,由於插入到集合中的每一個對象-鍵值對都必須產生相應的哈希代碼,因此項目插入的代價就有點高了。所以,HashTable 主要運用在按照任意鍵值反覆檢索大量相對靜態的數據這一場合下

Add方法參數都是object類型

對哈希表進行排序
     對哈希表進行排序在這裏的定義是對key/value鍵值對中的key按必定規則從新排列,可是實際上這個定義是不能實現的,由於咱們沒法直接在 Hashtable進行對key進行從新排列,若是須要Hashtable提供某種規則的輸出,能夠採用一種變通的作法:

ArrayList akeys=new ArrayList(ht.Keys); //別忘了導入System.Collections
akeys.Sort(); //按字母順序進行排序
foreach(string skey in akeys)
{
    Console.Write(skey + ":");
    Console.WriteLine(ht[skey]);//排序後輸出
}
 1 Hashtable ht = new Hashtable(); //建立一個Hashtable實例
 2 ht.Add("E", "e");//添加key/value鍵值對
 3 ht.Add("A", "a");
 4 ht.Add("C", "c");
 5 ht.Add("B", "b");
 6 string s = (string)ht["A"];
 7 if (ht.Contains("E")) //判斷哈希表是否包含特定鍵,其返回值爲true或false
 8     Console.WriteLine("the E key:exist");
 9 ht.Remove("C");//移除一個key/value鍵值對
10 Console.WriteLine(ht["A"]);//此處輸出a
11 ht.Clear();//移除全部元素
12 Console.WriteLine(ht["A"]); //此處將不會有任何輸出
13 Console.ReadKey();
View Code

NameValueCollection:

System.Collections.Specialized.NameValueCollection 最有趣的地方在於它能包含關聯同一鍵值的多個項目(容許出現相同的鍵,值會用逗號連起來),這正是它與其餘內建集合的差異所在。除此之外,它在功能上相似HashTable,按照源自每一項目鍵值的哈希代碼對項目排序從而也具備類同的優缺點。用處:寫自定義控件存儲鍵值通常用這個


 ListDictionary 和 HybridDictionary:

ListDictionary 和 HybridDictionary 類歸屬於System.Collections.Specialized。它們都在按照惟一鍵值的原則來組織項目,並且都實現了 IDictionary 和 ICollection 。ListDictionary在內部以鏈表的方式存儲項目,建議用在不會增加超過10個項目的集合中。HybridDictionary採用一個內部鏈 表(實際上就是ListDictionary)做爲小集合,當集合變得足夠大(超過10個項目)以致於鏈表實現效率下降時就會轉換爲HashTable。


 StringCollection 和 StringDictionary:

System.Collections.Specialized.StringCollection 和 System.Collections.Specialized.StringDictionary 都對存儲字符串的集合進行了優化。 StringCollection實現了 IList 和 ICollection 並且實質上就是ArrayList,只不過實現了強烈的類型化僅僅接受字符串而已。StringCollection最理想的應用場合是常常更新或增長的 少許數據,而StringDictionary則最適用於不常常增長項目到諸如HashTable之類集合中的大量數據。


 SortedList:

System.Collections.SortedList,它實現了IDictionary和ICollection接口,是最基本的排序集 合,與Vb6下的Collection對象很是相似。 SortedList存儲對象並按照關聯的鍵值對這些存儲對象排序。它們也是同時支持索引數字和鍵對象檢索的惟一內建的.NET集合,與哈希表相似,區別在於SortedList中的Key數組排好序的

 1 SortedList sl = new SortedList();
 2 sl["c"] = 41;
 3 sl["a"] = 42;
 4 sl["d"] = 11;
 5 sl["b"] = 13;
 6 
 7 foreach (DictionaryEntry element in sl)
 8 {
 9     string s = (string)element.Key;
10     int i = (int)element.Value;
11     Console.WriteLine("{0},{1}", s, i);
12 }
View Code

 Dictionary 泛型集合

泛型最多見的用途是泛型集合,命名空間System.Collections.Generic 中包含了一些基於泛型的集合類,使用泛型集合類能夠提供更高的類型安全性,還有更高的性能,避免了非泛型集合的重複的裝箱和拆箱。
    不少非泛型集合類都有對應的泛型集合類,下面是經常使用的非泛型集合類以及對應的泛型集合類:
非泛型集合類 泛型集合類
ArrayList List<T>
HashTable DIctionary<T>
Queue Queue<T>
Stack Stack<T>
SortedList SortedList<T>
 
咱們用的比較多的非泛型集合類主要有 ArrayList類 和 HashTable類。咱們常常用HashTable 來存儲將要寫入到數據庫或者返回的信息,在這之間要不斷的進行類型的轉化,增長了系統裝箱和拆箱的負擔,若是咱們操縱的數據類型相對肯定的化  用 Dictionary<TKey,TValue> 集合類來存儲數據就方便多了,例如咱們須要在電子商務網站中存儲用戶的購物車信息( 商品名,對應的商品個數)時,徹底能夠用 Dictionary<string, int> 來存儲購物車信息,而不須要任何的類型轉化。

    下面是簡單的例子,包括聲明,填充鍵值對,移除鍵值對,遍歷鍵值對
 1 Dictionary<string, string> myDic = new Dictionary<string, string>();
 2 myDic.Add("aaa", "111");
 3 myDic.Add("bbb", "222");
 4 myDic.Add("ccc", "333");
 5 myDic.Add("ddd", "444");
 6 //若是添加已經存在的鍵,add方法會拋出異常
 7 try
 8 {
 9     myDic.Add("ddd", "ddd");
10 }
11 catch (ArgumentException ex)
12 {
13     Console.WriteLine("此鍵已經存在:" + ex.Message);
14 }
15 //解決add()異常的方法是用ContainsKey()方法來判斷鍵是否存在
16 if (!myDic.ContainsKey("ddd"))
17 {
18     myDic.Add("ddd", "ddd");
19 }
20 else
21 {
22     Console.WriteLine("此鍵已經存在:");
23 
24 }
25 
26 //而使用索引器來負值時,若是建已經存在,就會修改已有的鍵的鍵值,而不會拋出異常
27 myDic["ddd"] = "ddd";
28 myDic["eee"] = "555";
29 
30 //使用索引器來取值時,若是鍵不存在就會引起異常
31 try
32 {
33     Console.WriteLine("不存在的鍵\"fff\"的鍵值爲:" + myDic["fff"]);
34 }
35 catch (KeyNotFoundException ex)
36 {
37     Console.WriteLine("沒有找到鍵引起異常:" + ex.Message);
38 }
39 //解決上面的異常的方法是使用ContarnsKey() 來判斷時候存在鍵,若是常常要取健值得化最好用 TryGetValue方法來獲取集合中的對應鍵值
40 string value = "";
41 if (myDic.TryGetValue("fff", out value))
42 {
43     Console.WriteLine("不存在的鍵\"fff\"的鍵值爲:" + value);
44 }
45 else
46 {
47     Console.WriteLine("沒有找到對應鍵的鍵值");
48 }
49 
50 //下面用foreach 來遍歷鍵值對
51 //泛型結構體 用來存儲健值對
52 foreach (KeyValuePair<string, string> kvp in myDic)
53 {
54     Console.WriteLine("key={0},value={1}", kvp.Key, kvp.Value);
55 }
56 //獲取值得集合
57 foreach (string s in myDic.Values)
58 {
59     Console.WriteLine("value={0}", s);
60 }
61 //獲取值得另外一種方式
62 Dictionary<string, string>.ValueCollection values = myDic.Values;
63 foreach (string s in values)
64 {
65     Console.WriteLine("value={0}", s);
66 }
View Code
相關文章
相關標籤/搜索