最近使用了Dictionary,出現了意想不到的錯誤,先記錄一下自己遇到的問題以及目前我的解決方法,然后溫習一下Dictionary的基礎用法。
一、自己遇到的問題
1、代碼如下:

namespace DictionaryExample { class Program { static void Main(string[] args) { string[] pedlarArray = {"小明","小王","小紅"}; Dictionary<Apple, string[]> appleMessageDict = new Dictionary<Apple, string[]>(); appleMessageDict.Add(new Apple() { Quantity = 20, Price = 5 }, pedlarArray); Console.WriteLine("appleMessageDict.Values.Count:{0}", appleMessageDict.Count); Console.WriteLine("appleMessageDict.Keys.Count:{0}", appleMessageDict.Keys.Count); Console.WriteLine("appleMessageDict.Values.Count:{0}", appleMessageDict.Values.Count); Console.ReadKey(); } } public class Apple { private int _quantity; private float _price; public int Quantity { get { return _quantity; } set { _quantity = value; } } public float Price { get { return _price; } set { _price = value; } } public Apple() { } } }
運行結果:
2、原因分析
代碼里面的字典AppleMessageDict的value是字符串數組類型,key是一個類類型。為什么數量都是1呢?通過分析我發現appleMessageDict.Count、appleMessageDict.Keys.Count、appleMessageDict.Values.Count這三個統計count的方式僅僅是針對於字典本身的統計,無關key/value是什么類型,僅僅是統計字典包含多少鍵值對。如果每個鍵值對中的key或者value是集合,想要知道key或者value的數量,需要另外處理。
3、目前我的解決方法

static void Main(string[] args) { int length = 0; string[] pedlarArray = { "小明", "小王", "小紅" }; Dictionary<Apple, string[]> appleMessageDict = new Dictionary<Apple, string[]>(); appleMessageDict.Add(new Apple() { Quantity = 20, Price = 5 }, pedlarArray); foreach (KeyValuePair<Apple, string[]> pair in appleMessageDict)//目前只能循環統計字典的數量
{ length += pair.Value.Length; } Console.WriteLine("appleMessageDict的數量:{0}", length); //Console.WriteLine("appleMessageDict.Values.Count:{0}", appleMessageDict.Count); //Console.WriteLine("appleMessageDict.Keys.Count:{0}", appleMessageDict.Keys.Count); //Console.WriteLine("appleMessageDict.Values.Count:{0}", appleMessageDict.Values.Count);
Console.ReadKey(); }
二、Dictionary的基礎知識
1、Dictionary的概念
Dictionary<[key], [value]>是一個泛型,Dictionary是一種變種的HashTable,是一個表示鍵和值的集合。
2、Dictionary的特點
鍵必須是唯一的,而值不需要唯一的 。鍵和值都可以是任何類型。通過一個鍵讀取一個值的時間是接近O(1) 。
3、Dictionary的構造函數
(1)Dictionary<TKey,TValue>()構造函數:初始化 Dictionary<TKey,TValue> 類的新實例,該實例為空,具有默認的初始容量(Dictionary的默認大小為3)並為鍵類型使用默認的相等比較器。key不可重復,區分大小寫

static void Main(string[] args) { #region Dictionary構造函數 (key不可重復,區分大小寫) Console.WriteLine("==========Dictionary普通構造函數! key不可重復,區分大小寫==========="); Dictionary<string, string> dict =new Dictionary<string, string>(); dict.Add("a","1"); try { dict.Add("A", "1"); foreach (KeyValuePair<string,string> item in dict) { Console.WriteLine("Key:{0},Value:{1}", item.Key,item.Value); } } catch (ArgumentException) { Console.WriteLine("鍵A已經存在!"); } Console.WriteLine("輸入任意值,執行下一個構造函數:"); Console.ReadKey(); }
運行結果
(2)Dictionary<TKey,TValue>(IEqualityComparer<TKey>)構造函數:初始化 Dictionary<TKey,TValue> 類的新實例,該實例為空,具有默認的初始容量(Dictionary的默認大小為3)並使用指定的 IEqualityComparer<T>。key不可重復,不區分大小寫

#region Dictionary 構造函數,key不可重復,不區分大小寫 Console.WriteLine("==========Dictionary構造函數! key不可重復,不區分大小寫==========="); Dictionary<string, string> dict1 = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); dict1.Add("a", "1"); try { dict1.Add("A", "1"); } catch (ArgumentException) { Console.WriteLine("鍵A已經存在!"); } Console.WriteLine("輸入任意值,執行下一個構造函數:"); Console.ReadKey(); #endregion
運行結果
(3)Dictionary<TKey,TValue>(IDictionary<TKey,TValue>)構造函數:初始化 Dictionary<TKey,TValue> 類的新實例,該實例包含從指定的 IDictionary<TKey,TValue> 復制的元素並為鍵類型使用默認的相等比較器。

#region Dictionary構造函數 通過SortedDictionary初始化Dictionary Console.WriteLine("==========Dictionary構造函數!通過SortedDictionary初始化Dictionary==========="); SortedDictionary<string, string> sortDict =new SortedDictionary<string, string>(); sortDict.Add("txt", "notepad.exe"); sortDict.Add("bmp", "paint.exe"); sortDict.Add("dib", "paint.exe"); sortDict.Add("rtf", "wordpad.exe"); Dictionary<string, string> copyDict = new Dictionary<string, string>(sortDict); foreach (KeyValuePair<string,string> item in copyDict) { Console.WriteLine("key:{0},value:{1}",item.Key,item.Value); } Console.WriteLine("輸入任意值,執行下一個構造函數:"); Console.ReadKey(); #endregion
運行結果
(4)Dictionary<TKey,TValue>(Int32)構造函數:初始化 Dictionary<TKey,TValue> 類的新實例,該實例為空,具有指定的初始容量並為鍵類型使用默認的相等比較器。
字典默認容量是3,可以初始化字典容量,如果初始化的容量不滿足實際需求,將自動增加容量(自動擴容的規則將在下邊專門介紹)。如果可以估計集合的大小,指定的初始容量,則無需要執行多個大小調整操作

#region Dictionary 構造函數 初始化Dictionary的大小容量 //字典默認容量是3,可以初始化字典容量,如果初始化的容量不滿足實際需求,將自動增加容量。如果可以估計集合的大小,指定的初始容量,則無需要執行多個大小調整操作 Console.WriteLine("==========Dictionary 構造函數 初始化Dictionary的大小容量==========="); Dictionary<string, string> capacityDict = new Dictionary<string, string>(2); capacityDict.Add("txt", "notepad.exe"); capacityDict.Add("bmp", "paint.exe"); capacityDict.Add("dib", "paint.exe"); capacityDict.Add("rtf", "wordpad.exe"); foreach (KeyValuePair<string, string> item in copyDict) { Console.WriteLine("key:{0},value:{1}", item.Key, item.Value); } Console.WriteLine("輸入任意值,執行下一個構造函數:"); Console.ReadKey(); #endregion
運行結果
(5)Dictionary<TKey,TValue>(IDictionary<TKey,TValue>, IEqualityComparer<TKey>)構造函數:使用不區分大小寫的比較器創建一個新的字典和填充從一個字典,其中使用區分大小寫的比較器,如本示例所示的條目時如果輸入的字典具有僅大小寫不同的鍵,則會發生異常。

#region Dictionary 構造函數 使用不區分大小寫的比較器創建一個新的字典和填充從一個字典 //使用不區分大小寫的比較器創建一個新的字典和填充從一個字典,其中使用區分大小寫的比較器,如本示例所示的條目時如果輸入的字典具有僅大小寫不同的鍵,則會發生異常。 Console.WriteLine("==========Dictionary 構造函數 使用不區分大小寫的比較器創建一個新的字典和填充從一個字典==========="); SortedDictionary<string, string> sortDict1 = new SortedDictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); sortDict1.Add("txt", "notepad.exe"); sortDict1.Add("bmp", "paint.exe"); sortDict1.Add("dib", "paint.exe"); sortDict1.Add("rtf", "wordpad.exe"); Dictionary<string, string> copyDict1 = new Dictionary<string, string>(sortDict1,StringComparer.CurrentCultureIgnoreCase); foreach (KeyValuePair<string, string> item in copyDict1) { Console.WriteLine("key:{0},value:{1}", item.Key, item.Value); } Console.WriteLine("輸入任意值,執行下一個構造函數:"); Console.ReadKey(); #endregion #region Dictionary 構造函數 使用不區分大小寫的比較器創建一個新的字典和填充從一個字典 //使用不區分大小寫的比較器創建一個新的字典和填充從一個字典,其中使用區分大小寫的比較器,如本示例所示的條目時如果輸入的字典具有僅大小寫不同的鍵,則會發生異常。 Console.WriteLine("==========Dictionary 構造函數 使用不區分大小寫的比較器創建一個新的字典和填充從一個字典==========="); SortedDictionary<string, string> sortDict1 = new SortedDictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); sortDict1.Add("txt", "notepad.exe"); sortDict1.Add("bmp", "paint.exe"); sortDict1.Add("dib", "paint.exe"); sortDict1.Add("rtf", "wordpad.exe"); Dictionary<string, string> copyDict1 = new Dictionary<string, string>(sortDict1,StringComparer.CurrentCultureIgnoreCase); foreach (KeyValuePair<string, string> item in copyDict1) { Console.WriteLine("key:{0},value:{1}", item.Key, item.Value); } Console.WriteLine("輸入任意值,執行下一個構造函數:"); Console.ReadKey(); #endregion
運行結果
(6)Dictionary<TKey,TValue>(Int32, IEqualityComparer<TKey>)構造函數:初始化 Dictionary<TKey,TValue> 類的新實例,該實例為空,具有指定的初始容量並使用指定的 IEqualityComparer<T>。

#region Dictionary<string, string> dict3 =new Dictionary<string, string>(5,StringComparer.CurrentCultureIgnoreCase); dict3.Add("txt", "notepad.exe"); dict3.Add("bmp", "paint.exe"); dict3.Add("DIB", "paint.exe"); dict3.Add("rtf", "wordpad.exe"); try { dict3.Add("BMP", "paint.exe"); } catch (ArgumentException) { Console.WriteLine("\nBMP is already in the dictionary."); } foreach (KeyValuePair<string, string> kvp in dict3) { Console.WriteLine("Key = {0}, Value = {1}", kvp.Key,kvp.Value); } #endregion
運行結果
(7)Dictionary<TKey,TValue>(SerializationInfo, StreamingContext)構造函數:用序列化數據初始化 Dictionary<TKey,TValue> 類的新實例。
===============List和Dictionary擴容規則======================
List和Dictionary的構造函數都有一個入參為int的構造函數:public Dictionary(int capacity);和public List(int capacity);capacity用來指定List和Dictionary的初始容量。List和Dictionary的內部實現方式都是使用數組,因為數組的容量是固定的,所以初始化的時候就會對其申請內存,List的默認大小為4,Dictionary的默認大小為3。如果不指定初始化容量,系統會默認創建。當往容器里Add數據時,如果當前數組已滿,就會新創建一塊兩倍於當前數組長度的內存,把原有數據copy到新內存中,再繼續往里添加。
舉個例子,創建一個沒指定初始容量的List后,依次往里面添加了12個元素,內存是這樣分配的:首先默認分配了一塊長度為4個List元素的內存給List數組,當添加到第五個時,發現長度不足,所以會分配4*2=8的內存,把原有的4個數據拷貝到新內存塊中並把第五個元素添加到末尾;當添加到第九個時,也會重新分配一個8*2=16的內存,把原有的8個數據拷貝到新內存塊中並把第9個元素添加到末尾;所以最終List數組的長度為16,里面存放了12個元素,共分配了三次內存,進行了兩次的內存拷貝。
Dictionary容量分配規則也是如此。
所以合理的指定初始容量,可以減少內存的分配和拷貝次數,甚至還能節省內存空間。
================================================================
4、Dictionary的常用屬性
5、Dictionary的方法
(1)添加元素
第一種方式:通過Add()添加元素
Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict.Add("key"+i,"value"+i); }
第二種方式(推薦):通過中括號添加元素
Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict["key" + i]="value"+i; }
為什么我們推薦第二種方式呢?我們知道字典的鍵值不可重復(不使用StringComparer.CurrentCultureIgnoreCase指定的情況下),使用Add()方式,如果存在相同的鍵,會直接拋出ArgumentException異常。而使用第二種方式,如果存在相同的鍵,只要key不是null,就會進行改寫,就不會出現異常。
(2)查找元素:第一種方式:通過key查找元素

static void Main(string[] args) { Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict["key" + i]="value"+i; } if (messageDict.ContainsKey("key4")) { Console.WriteLine("key是{0},value是{1}","key4",messageDict["key4"]); } Console.ReadKey(); }
(3)遍歷字典:通過KeyValuePair遍歷元素

static void Main(string[] args) { Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict["key" + i]="value"+i; } foreach (KeyValuePair<string,string> item in messageDict) { Console.WriteLine("key是{0},value是{1}", item.Key,item.Value); } Console.ReadKey(); }
(4)僅遍歷所有的values

static void Main(string[] args) { Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict["key" + i]="value"+i; } Dictionary<string, string>.ValueCollection valueCollection =messageDict.Values; if (valueCollection!= null&&valueCollection.Count>0) { foreach (string item in valueCollection) { Console.WriteLine("value是{0}", item); } } Console.ReadKey(); }
(5)僅遍歷所有的keys

static void Main(string[] args) { Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict["key" + i]="value"+i; } Dictionary<string, string>.KeyCollection keyCollection =messageDict.Keys; if (keyCollection != null&& keyCollection.Count>0) { foreach (string item in keyCollection) { Console.WriteLine("key是{0}", item); } } Console.ReadKey(); }
(6)通過Remove移除元素

static void Main(string[] args) { Dictionary<string, string> messageDict = new Dictionary<string, string>(); for (int i = 0; i < 10; i++) { messageDict["key" + i]="value"+i; } messageDict.Remove("key0"); foreach (KeyValuePair<string,string> item in messageDict) { Console.WriteLine("key是{0},value是{1}", item.Key,item.Value); } Console.ReadKey(); }