【轉】C#集合類型大盤點

来源:https://www.cnblogs.com/RYouHoo-923/archive/2018/01/12/8275551.html
-Advertisement-
Play Games

C#集體類型( Collections in C#) 集合是.NET FCL(Framework Class Library)中很重要的一部分,也是我們開發當中最常用到的功能之一,幾乎是無處不在。俗話說知其然,知其所以然,平常看到IEnumerable,IEnumerator,ICollection ...


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);
}

  

ICollection<T>和ICollection

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

  1. 統計集合和元素個數
  2. 獲取元素的下標
  3. 判斷是否存在
  4. 添加元素到未尾
  5. 移除元素等等。。。

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

IList<T>和IList

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

IReadOnlyList<T>

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

IDictionary<TKey,TValue>

  IDictionary提供了對鍵值對集合的訪問,也是繼承了ICollection<T>和IEnumerable,擴展了通過Key來訪問和操作數據的方法。

關聯性泛型集合類

  關聯性集合類即我們常說的鍵值對集合,允許我們通過Key來訪問和維護集合。我們先來看一下 FCL為我們提供了哪些泛型的關聯性集合類:

  1. Dictionary<TKey,TValue>
  2. SortedDictionary<TKey,TValue>
  3. SortedList<TKey,TValue>

Dictionary<TKey,TValue>

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

  Dictionary<TKey,TValue>優勢是查找插入速度快,那麼什麼是它的劣勢呢?因為採用Hashtable作為存儲結構,就意味著裡面的數據是無序排列的,所以想按一定的順序去遍歷Dictionary<TKey,TValue>裡面的數據是要費一點工夫的。

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

SortedDictioanry<TKey,TValue>

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

SortedList<TKey,TValue>       

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

非關聯性泛型集合類

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

  1. List<T>
  2. LinkedList<T>
  3. HashSet<T>
  4. SortedSet<T>
  5. Stack<T>
  6. Queue<T>

List<T>

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

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

LinkedList<T>

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

HashSet<T>

  HashSet是一個無序的能夠保持唯一性的集合。我們也可以把HashSet看作是Dictionary<TKey,TValue>,只不過TKey和TValue都指向同一個對象。HashSet非常適合在我們需要保持集合內元素唯一性但又不需要按順序排列的時候。

  HashSet不支持下標訪問。

SortedSet<T>

  SortedSet和HashSet,就像SortedDictionary和Dictionary一樣,還記得這兩個的區別麽?SortedSet內部也是一個二叉樹,用來支持按順序的排列元素。

Stack<T>

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

Queu<T>

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

推薦使用場景

 

集合

順序排列

連順存儲

直接訪問方式

訪問時間

操作時間

備註

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

後來被List<T>替代。

  1. HashTable 後來被Dictionary<TKey,TValue>替代。
  2. Queue 後來被Queue<T>替代。
  3. SortedList 後來被SortedList<T>替代。
  4. Stack 後來被Stack<T>替代。

線程安全的集合類

  1. ConcurrentQueue 線程安全版本的Queue
  2. ConcurrentStack線程安全版本的Stack
  3. ConcurrentBag線程安全的對象集合
  4. ConcurrentDictionary線程安全的Dictionary
  5. BlockingCollection

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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.什麼是RESTful? REST,即Representational State Transfer的縮寫。"(資源的)表現層狀態轉化"。 2.什麼是表現層? "資源"具體呈現出來的形式,叫做它的"表現層"(Representation)。比如,文本可以用txt格式表現,也可以用HTML格式、XM ...
  • 創建響應式WinForm應用程式並不那麼簡單。 響應式佈局,我們在此指的是在不同屏幕解析度下的可用性。 對於WinForm應用程式,我們需要明確地根據解析度來調整控制項的大小和重新定位。 雖然在使用WPF時有相關的實踐應用,通過使用控制項的docking和anchoring,或使用panels等方法,但... ...
  • BlockingCollection集合是一個擁有阻塞功能的集合,它就是完成了經典生產者消費者的演算法功能。一般情況下,我們可以基於 生產者 - 消費者模式來實現併發。BlockingCollection<T> 類是最好的解決方案 剛結束的物聯網卡項目,我需要調用移動的某個具有批量獲取物聯網卡數據的接 ...
  • 概述 在之前寫的一篇關於async和await的前世今生的文章之後,大家似乎在async和await提高網站處理能力方面還有一些疑問,博客園本身也做了不少的嘗試。今天我們再來回答一下這個問題,同時我們會做一個async和await在WinForm中的嘗試,並且對比在4.5之前的非同步編程模式APM/E ...
  • 一個網頁,它是顯示圖片,但在一些瀏覽器,它卻顯示如下: Insus.NET猜,不是瀏覽器不相容,就是代碼有問題。 在代碼中,只是輸出數據流,圖片格式很多種,如jpg,png,bmp等,沒有指定,程式也不清楚要顯示什麼格式的圖片。因此,Insus.NET把代碼改為如下: context.Respons ...
  • 為什麼要學習表達式樹?表達式樹是將我們原來可以直接由代碼編寫的邏輯以表達式的方式存儲在樹狀的結構里,從而可以在運行時去解析這個樹,然後執行,實現動態的編輯和執行代碼。LINQ to SQL就是通過把表達式樹翻譯成SQL來實現的,所以瞭解表達樹有助於我們更好的理解 LINQ to SQL,同時如果你有 ...
  • 快樂的Lambda表達式 上一篇 背後的故事之 - 快樂的Lambda表達式(一)我們由淺入深的分析了一下Lambda表達式。知道了它和委托以及普通方法的區別,並且通過測試對比他們之間的性能,然後我們通過IL代碼深入瞭解了Lambda表達式,以及介紹瞭如何在.NET中用Lambda表達式來實現Jav ...
  • 快樂的Lambda表達式(二) 自從Lambda隨.NET Framework3.5出現在.NET開發者眼前以來,它已經給我們帶來了太多的欣喜。它優雅,對開發者更友好,能提高開發效率,天啊!它還有可能降低發生一些潛在錯誤的可能。LINQ包括ASP.NET MVC中的很多功能都是用Lambda實現的。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...