C#语言本身没有字典和哈希表这个类型,他们存在于 .NET 框架的基础库中(System.Collection.Generic)。字典与哈希表相对数组最大的优势在于读取的复杂度可以降到O(1),但是会使用更多的内存空间,所以就是一种用内存空间换取时间的方法。

字典与哈希表的异同

相同点

  • 都是以键值对的形式存在,键必须是唯一的,值不需要是唯一的,都是无序的键值对。
  • 存数据的个数也不受限制。
  • 都可以以foreach遍历存数据的个数,不受限。
  • 方法很相似。

不同点

  • 键与值的类型不同:字典取决于定义字典时的设置类型(参照实现方式不同来理解)
  • 命名空间不同:字典类型(Dictionary)位于 System.Collections.Generic 命名空间中,哈希表类型(Hashtable)位于 System.Collections 命名空间中
  • 限制类型不同:字典存储数据限制了类型,哈希表不限制类型(参照实现方式不同来理解)
  • 实现方式不同:Hastable 是哈希表的实现,能根据关键字取关键值,这 key 的类型是 object , value 的类型也是object。Dictionary<Tkey,Tvalue>是Hastbale的泛型实现。
  • 添加数据时Hashtable快。频繁调用数据时Dictionary快。

哈希表(Hashtable)

使用条件

  • 包含名空间 System.Collection.Generic
  • Hashtable 里面的每一个元素都是一个键值对(由二个元素组成:键和值)
  • 键必须是唯一的,而值不需要唯一的
  • 键和值都可以是任何类型(比如:string, int, 自定义类型,等等),且同一个哈希表可以使用多种数据类型
  • 通过一个键读取一个值的时间是接近O(1)
  • 键值对之间的偏序可以不定义

示例

using System;
using System.Collections;

namespace TestHashtableAndDictionary
{
    class Program
    {
        static void Main(string[] args)
        {
            // 哈希表的定义
            Hashtable ht = new Hashtable();

            // 向哈希表中添加元素(key和value都可以为任意类型)
            ht.Add(1, "Hello");
            ht.Add("James", 32);
            ht.Add("Color", "blue");
			
			// 当添加元素为重复的key时,应采用判断是否直接修改
			if (ht.ContainsKey(1))
			{
				ht[1] = "重复的key就直接修改value";
			}
			else
			{
				ht.Add(1, "不是重复的key");
			}

            // 获取value
            Console.WriteLine("The value of ht[1]: {0}", ht[1]);

            // 修改value
            ht[1] = "James";
            ht["James"] = 23;

            // 判断键是否存在
            if (ht.ContainsKey(1))
            {
                Console.WriteLine("The Dictionary contains key 1");
            }

            // 判断值是否存在
            if (ht.ContainsValue("World"))
            {
                Console.WriteLine("The httionary contain Value 'World'");
            }

            // 遍历key(当key的类型不同时强制转换为了int)
            foreach (int key in ht.Keys)
            {
                Console.WriteLine("Key = {0}", key);
            }

            // 遍历value(当value为不同类型时强制转换为了string)
            foreach (string value in ht.Values)
            {
                Console.WriteLine("Key = {0}", value);
            }

            // 同时遍历key和value
            foreach (DictionaryEntry de in ht)
            {
                Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
            }

            // 删除元素
            ht.Remove(1);

            // 清空哈希表中的元素
            ht.Clear();
            Console.ReadKey();
        }
    }
}

常用属性

名称 说明
Count 获取 Hashtable 中包含的键值对个数。
IsFixedSize 获取一个值,表示 Hashtable 是否具有固定大小。
IsReadOnly 获取一个值,表示 Hashtable 是否只读。
Item 获取或设置与指定的键相关的值。
Keys 获取一个 ICollection,包含 Hashtable 中的键。
Values 获取一个 ICollection,包含 Hashtable 中的值。

常用方法

名称 说明
public virtual void Add(object key,object value) 向 Hashtable 添加一个带有指定的键和值的元素。
public virtual void Clear() 从 Hashtable 中移除所有的元素。
public virtual bool ContainsKey(object key) 判断 Hashtable 是否包含指定的键。
public virtual bool ContainsValue(object value) 判断 Hashtable 是否包含指定的值。
public virtual void Remove(object key) 从 Hashtable 中移除带有指定的键的元素。

字典(Dictionary)

使用条件

  • 包含名空间 System.Collection.Generic
  • Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值)
  • 键必须是唯一的,而值不需要唯一的
  • 键和值都可以是任何类型(比如:string, int, 自定义类型,等等),但是需要定义
  • 通过一个键读取一个值的时间是接近O(1)
  • 键值对之间的偏序可以不定义

示例

using System;
using System.Collections.Generic;

namespace TestHashtableAndDictionary
{
    class Program
    {
        static void Main(string[] args)
        {
            // 字典的定义
            Dictionary<int, string> dic = new Dictionary<int, string>();
            // 向字典中添加元素(键值对的类型要与定义中的类型相同)
            dic.Add(1, "Hello");
            dic.Add(2, "World");
            dic.Add(3, "!");

            // 获取value
            Console.WriteLine("The value of dic[1]: {0}", dic[1]);

            // 修改value
            dic[1] = "James";

            // 根据value获取key(循环方式,还可以采用LINQ查询方式)
            foreach (int key in dic.Keys)
            {
                if (dic[key].Equals("World"))
                {
                    Console.WriteLine("The Key to the value 'World' is 1");
                }
            }

            // 判断键是否存在
            if (dic.ContainsKey(1))
            {
                Console.WriteLine("The Dictionary contains key 1");
            }

            // 判断值是否存在
            if (dic.ContainsValue("World"))
            {
                Console.WriteLine("The Dictionary contain Value 'World'");
            }

            // 遍历key
            foreach (int key in dic.Keys)
            {
                Console.WriteLine("Key = {0}", key);
            }

            // 遍历value
            foreach (string value in dic.Values)
            {
                Console.WriteLine("Key = {0}", value);
            }

            // 遍历字典(遍历key,再通过key来读取value)
            foreach (int key in dic.Keys)
            {
                Console.WriteLine("key: {0}, value: {1}", key, dic[key]);
            }

            // 同时遍历key和value
            foreach (KeyValuePair<int, string> kvp in dic)
            {
                Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
            }

            // 删除元素
            dic.Remove(1);

            // 清空字典中的元素
            dic.Clear();
            Console.ReadKey();
        }
    }
}

常用属性

名称 说明
Comparer 获取用于确定字典中的键是否相等的 IEqualityComparer
Count 获取包含在 Dictionary<TKey, TValue> 中的键/值对的数目
Item 获取或设置与指定的键相关联的值
Keys 获取包含 Dictionary<TKey, TValue> 中的键的集合
Values 获取包含 Dictionary<TKey, TValue> 中的值的集合

常用方法

名称 说明
Add 将指定的键和值添加到字典中。
Clear 从 Dictionary<TKey, TValue> 中移除所有的键和值。
ContainsKey 确定 Dictionary<TKey, TValue> 是否包含指定的键。
ContainsValue 确定 Dictionary<TKey, TValue> 是否包含特定值。
Equals(Object) 确定指定的 Object 是否等于当前的 Object。 (继承自 Object。)
Finalize 允许对象在“垃圾回收”回收之前尝试释放资源并执行其他清理操作。 (继承自 Object。)
GetEnumerator 返回循环访问 Dictionary<TKey, TValue> 的枚举器。
GetHashCode 用作特定类型的哈希函数。 (继承自 Object。)
GetObjectData 实现 System.Runtime.Serialization.ISerializable 接口,并返回序列化 Dictionary<TKey, TValue> 实例所需的数据。
GetType 获取当前实例的 Type。 (继承自 Object。)
MemberwiseClone 创建当前 Object 的浅表副本。 (继承自 Object。)
OnDeserialization 实现 System.Runtime.Serialization.ISerializable 接口,并在完成反序列化之后引发反序列化事件。
Remove 从 Dictionary<TKey, TValue> 中移除所指定的键的值。
ToString 返回表示当前对象的字符串。 (继承自 Object。)
TryGetValue 获取与指定的键相关联的值。

建议使用场景

  • 单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分.
  • 多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized()方法可以获得完全线程安全的类型. 而Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减.
  • Dictionary 有按插入顺序排列数据的特性 (注: 但当调用 Remove() 删除过节点后顺序被打乱), 因此在需要体现顺序的情境中使用 Dictionary 能获得一定方便.
  • 如下的情景应优先采用哈希表:
    1. 某些数据会被高频率查询
    2. 数据量大
    3. 查询字段包含字符串类型
    4. 数据类型不唯一