C#基础知识之Dictionary

最近使用了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()
        {
        }
    }
}   
View Code

  运行结果:

  

  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();
}                 
View Code

二、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();   
}         
View Code

    运行结果

    


  (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  
View Code

     

    运行结果

    

    (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
View Code

       

    运行结果

    


    (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   
View Code

    运行结果

    


    (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  
View Code

      
    运行结果

    

    (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("
BMP is already in the dictionary.");
}   
foreach (KeyValuePair<string, string> kvp in dict3)
{
    Console.WriteLine("Key = {0}, Value = {1}", kvp.Key,kvp.Value);
}
#endregion   
View Code

   

    运行结果

    

    (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();
}        
View Code

  
    (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();
}      
View Code

   


    (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();
}            
View Code

    (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();
}               
View Code

    (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();
}     
View Code

 

 

原文地址:https://www.cnblogs.com/qtiger/p/11176537.html