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 能获得一定方便.
- 如下的情景应优先采用哈希表:
- 某些数据会被高频率查询
- 数据量大
- 查询字段包含字符串类型
- 数据类型不唯一