首頁 >後端開發 >C#.Net教程 >詳解C#集合類型大盤點的圖文程式碼

詳解C#集合類型大盤點的圖文程式碼

黄舟
黄舟原創
2017-03-09 15:23:192047瀏覽

  C#集體型別( Collections in C#)

#   集合是.NET FCL(Framework Class Library)中很重要的一部分,也是我們開發當中最常使用到的功能之一,幾乎是無所不在。俗話說知其然,知其所以然,平常看到IEnumerable,IEnumerator,ICollection是不是知道他們之間各自的差別?除了List和Dictionary以外,你還用過哪些它的集合類別?廢話少說,今天我們就來看一些這些定義集合類別的介面以及他們的實作。

 集合介面

  先來看一下,FCL為我們提供了哪些介面:

#   

  IEnumerable 和IEnumberator

public interface IEnumerator
{
 
    bool MoveNext();
    object Current {  get; }
    void Reset();
}

  IEnumerator定義了我們遍歷集合的基本方法,以便我們可以實現單向向前的訪問集合中的每一個元素。而IEnumerable只有一個方法GetEnumerator即得到遍歷器。

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

  注意:我們常用的foreach即是一種語法糖,實際上還是呼叫Enumerator裡面的Current和MoveNext實現的遍歷功能。

List<string> list = new List<string>() 
{ 
    "Jesse",
    "Chloe",
    "Lei",
    "Jim",
    "XiaoJun"
};
 
// Iterate the list by using foreach
foreach (var buddy in list)
{
    Console.WriteLine(buddy);
}
 
// Iterate the list by using enumerator
List<string>.Enumerator enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    Console.WriteLine(enumerator.Current);
}

  上面的程式碼中所使用的foreach和enumerator到IL中最後都會被翻譯成enumerator的MoveNext和Current。

#

#   IEnumerable是一個很有用的接口,實作它的好處包括:

  1. 支援foreach語句


  2. # 作為一個標準的集合類別與其它類別庫進行互動


  3. # 滿足更複雜的集合介面的需求


  4. # 支援集合初始化器

  當然實作的方法也有很多,如下:

  1. 如果我們集合是透過封裝其它集合類別而來的,那麼我們可以直接傳回這個集合的enumerator


  2. 透過yield return 來回傳


  3. 實作我們自己的IEnumerator來實作

#   這裡為大家示範如何透過yield來實現返回enumerator

public class BuddyList : IEnumerable
{
    private string[] data= new string[]
    { 
        "Jesse",
        "Chloe",
        "Lei",
        "Jim",
        "XiaoJun"
    };
 
    public IEnumerator GetEnumerator()
    {
        foreach (var str in data)
        {
            yield return str;
        }
    }
}
 
var myBuddies= new BuddyList();
foreach (var str in myBuddies)
{
    Console.WriteLine(str);
}

  ICollection8742468051c85b06f0a0af9e3e506b5c和ICollection

  從最上面第一張圖我們可以知道,ICollection是直接繼承自IEnumerable。而實際上也是如此,我們可以說ICollection比IEnumerable多支援一些功能,不只提供基本的遍歷功能,還包括:

  1. 統計集合與元素數量


  2. # 取得元素的下標


  3. # 判斷是否存在


  4. # 將元素新增至未尾


  5. # 移除元素等等。 。 。

   ICollection 與ICollection8742468051c85b06f0a0af9e3e506b5c 略有不同,ICollection不提供編輯集合的功能,即Add和Remove。包括檢查元素是否存在Contains也不支援。

  IList8742468051c85b06f0a0af9e3e506b5c和IList

#   IList則是直接繼承自ICollection和IEnumerable。所以它包括兩者的功能,並且支援根據下標存取和添加元素。 IndexOf, Insert, RemoveAt等等。我們可以這樣說,IEnumerable支援的功能最少,只有遍歷。而ICollection支援的功能稍微多一點,不但有遍歷還有維護這個集合的功能。而IList是最全的版本。

  IReadOnlyList8742468051c85b06f0a0af9e3e506b5c

#   這個是在Framework4.5中新增的介面類型,可以被看作是IList8742468051c85b06f0a0af9e3e506b5c的縮減版,去掉了所有可能更改這個集合的功能。例如:Add, RemoveAt等等。

  IDictionary8c189faf63255a5ea96468ba21dd0564

  IDictionary提供了對鍵值對集合的訪問,也是繼承了ICollection8742468051c85b06f0a0af9e3e506b5c和IEnumerable,擴展了透過Key來存取和操作資料的方法。

 關聯性泛型集合類別

#   關聯性集合類別即我們常說的鍵值對集合,允許我們透過Key來存取和維護集合。我們先來看看 FCL為我們提供了哪些泛型的關聯性集合類別:

  1. # Dictionary8c189faf63255a5ea96468ba21dd0564


  2. # SortedDictionary8c189faf63255a5ea96468ba21dd0564


  3. SortedList8c189faf63255a5ea96468ba21dd0564

#   Dictionary8c189faf63255a5ea96468ba21dd0564

Dictionary8c189faf63255a5ea96468ba21dd0564可能是我們最常用的關聯性集合了,它的訪問,添加,刪除數據所花費的時間是所有集合類裡面最快的,因為它內部用了Hashtable作為存儲結構,所以不管儲存了多少鍵值對,查詢/新增/刪除所花費的時間都是一樣的,它的時間複雜度是O(1)。

  Dictionary8c189faf63255a5ea96468ba21dd0564優點是找出插入速度快,那麼什麼是它的缺點呢?因為採用Hashtable作為儲存結構,就表示裡面的資料是無序排列的,所以想按一定的順序去遍歷Dictionary8c189faf63255a5ea96468ba21dd0564裡面的資料是要花一點工夫的。

作為TKey的類型必須實作GetHashCode()Equals() 或提供一個IEqualityComparer#否則操作可能會出現問題。

  SortedDictioanry8c189faf63255a5ea96468ba21dd0564

#   SortedDictionary8c189faf63255a5ea96468ba21dd0564和Dictionary8c189faf63255a5ea96468ba21dd0564大致上是類似的,但是在實作方式上有一點點區別。 SortedDictionary8c189faf63255a5ea96468ba21dd0564用二元樹作為儲存結構的。並且按key的順序排列。那麼這樣的話SortedDictionary8c189faf63255a5ea96468ba21dd0564的TKey就必須實作IComparable7c013bef549e108856916cfbe0707d60#。 如果想要快速查詢的同時又能很好的支援排序的話,那就使用SortedDictionary吧。

  SortedList8c189faf63255a5ea96468ba21dd0564       

#   SortedList8c189faf63255a5ea96468ba21dd0564是另一個支援排序的關聯性集合。但不同的地方在於,SortedList實際上是將資料存儲存在陣列中的。也就是說添加和移除操作都是線性的,時間複雜度是O(n),因為操作其中的元素可能導致所有的資料移動。但是因為在查找的時候利用了二分搜索,所以查找的性能會好一些,時間複雜度是O(log n)。所以推薦使用場景是這樣地:如果你想要快速查找,又想集合按照key的順序排列,最後這個集合的操作(添加和移除)比較少的話,就是SortedList了。

 非關聯性泛型集合類別

#   非關聯性集合就是不用key運算的一些集合類,通常我們可以用元素本身或下標來運算。 FCL主要為我們提供了以下幾種非關聯性的泛型集合類別。

  1. List8742468051c85b06f0a0af9e3e506b5c


  2. # LinkedList8742468051c85b06f0a0af9e3e506b5c


  3. # HashSet8742468051c85b06f0a0af9e3e506b5c


  4. # SortedSet8742468051c85b06f0a0af9e3e506b5c


  5. # Stack8742468051c85b06f0a0af9e3e506b5c


  6. # Queue8742468051c85b06f0a0af9e3e506b5c

  List8742468051c85b06f0a0af9e3e506b5c

泛型的List 類別提供了不限制長度的集合類型,List在內部維護了一定長度的陣列(預設初始長度是4),當我們插入元素的長度超過4或初始長度的時候,會去重新建立一個新的數組,這個新數組的長度是初始長度的2倍(不永遠是2倍,當發現不斷的要擴充的時候,倍數會變大),然後把原來的數組拷貝過來。所以如果知道我們將要用這個集合裝多少個元素的話,可以在創建的時候指定初始值,這樣就避免了重複的創建新數組和拷貝值。

  另外的話由於內部實質是一個數組,所以在List的未必添加數據是比較快的,但是如果在數據的頭或者中間添加刪除數據相對來說更低效一些因為會影響其它數據的重新排列。

  LinkedList8742468051c85b06f0a0af9e3e506b5c

#   LinkedList在內部維護了一個雙向的鍊錶,也就是說我們在LinkedList的任何位置添加或刪除資料其效能都是很快的。因為它不會導致其它元素的移動。一般情況下List已經夠我們使用了,但是如果對這個集合在中間的添加刪除操作非常頻繁的話,就建議使用LinkedList。

  HashSet8742468051c85b06f0a0af9e3e506b5c

#   HashSet是一個無序的能夠保持唯一性的集合。我們也可以把HashSet看成是Dictionary8c189faf63255a5ea96468ba21dd0564,只不過TKey和TValue都指向同一個物件。 HashSet非常適合在我們需要保持集合內元素唯一性但又不需要依序排列的時候。

  HashSet不支援下標存取。

  SortedSet8742468051c85b06f0a0af9e3e506b5c

#   SortedSet和HashSet,就像SortedDictionary和Dictionary一樣,還記得這兩個的差別麼? SortedSet內部也是二元樹,用來支援依序的排列元素。

  Stack8742468051c85b06f0a0af9e3e506b5c

  後進先出的隊列
  不支援按下標訪問

  Queu8742468051c85b06f0a0af9e3e506b5c

#   先進先出的隊列
  不支援按下標訪問

 推薦使用場景

#

集合

順序排列

連順存儲

直接存取方式

訪問時間

操作時間

備註

#

Dictionary

 

Key

Key:

O(1)

 

O(1)

存取效能最快,不支援排序

#

SortedDinctionary

順序排列

Key

Key: 
O(log n)

O(log n)

快速存取和支援排序的折衷

#

SortedList

順序排列

Key

Key:

O(log n)

 

O(n)

和SortedDictionary相似,只是內部以資料取代樹作為儲存結構。

#

List

使用者可以精確控制元素的位置

Index

Index: O(1)

Value: O(n)

 

O(n)

最適合需要直接存取每一個元素的少量集合。

#

LinkedList

使用者可以精確控制元素的位置

不支援

Value:

O(n)

 

O(1)

最適合不需要直接存取單一元素,但是在集合中添加/移除非常頻繁的場景。

#

HashSet

不支援

Key

Key:

O(1)

 

O(1)

能保持元素唯一性的集合。不支援排序

#

SortedSet

順序排列

Key

Key:

O(log n)

 

O(log n)

能保持元素唯一性並且支援排序。

#

Stack

LIFO

只能取得頂部元素

Top: O(1)

O(1)

 

#

Queue

FIFO

只能獲底部元素

Front: O(1)

O(1)

 

 非泛型類別集合

#   泛型集合類別是在.NET2.0的時候出來的,也就是說在1.0的時候是沒有這麼方便的東西的。現在基本上我們已經不使用這些集合類別了,除非在做一些和舊程式碼保持相容的工作的時候。來看看1.0時代的.NET程式設計師們都有哪些集合類別可以用。

  1. ArraryList

#   後來被List8742468051c85b06f0a0af9e3e506b5c取代。

  1. HashTable 後來被Dictionary8c189faf63255a5ea96468ba21dd0564取代。


  2. # Queue 後來被Queue8742468051c85b06f0a0af9e3e506b5c取代。


  3. # SortedList 後來被SortedList8742468051c85b06f0a0af9e3e506b5c取代。


  4. # Stack 後來被Stack8742468051c85b06f0a0af9e3e506b5c取代。

 線程安全的集合類別

  1. ConcurrentQueue 線程安全版本的Queue


  2. # ConcurrentStack執行緒安全版本的Stack


  3. # ConcurrentBag執行緒安全的物件集合


  4. # ConcurrentDictionary線程安全的Dictionary


  5. # BlockingCollection

#   .NET為我們提供的集合類別是我們很常用的工具類別之一,希望這篇文章能幫助大家更好的認識這些集合類別。當然,個人感覺還有不完美的地方,比如說HashTable和Binary Search Tree就沒有細究下去,包括單向鍊錶和雙向鍊錶之間的比較本文也沒有提及。有興趣的朋友可以深入了解一下。

以上是詳解C#集合類型大盤點的圖文程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn