C# Dictionary, SortedDictionary, SortedList
就我个人觉得Dictionary, SortedDictionary, SortedList 这几个类的使用是比较简单的,只要稍微花点时间在网上查找一点资料,然后在阅读以下源码就理解的很清楚了。为什么要写这一片文章了,看一下code吧:
Dictionary<int, object> dict = new Dictionary<int, object>();
//load data to dict
int key = 1;
object obj = null;
if (dict.ContainsKey(key))
{
obj = dict[key];
}
本来程序在初始化的时候会初始化一个Dictionary,然后在程序很多地方需要读Dictionary,然后一同事刚开始就是这样写的code,后来说字典查找ContainsKey比较慢,所以就改为SortedDictionary,按照key排序的字典。 而我一般是用普通的Dictionary的 dict.TryGetValue(key, out obj)方法就可以了。所以就有了这篇文章,先说一下 结论吧:
Dictionary<TKey,TValue>泛型类提供了从一组键到一组值的映射。字典中的每个添加项都由一个值及其相关联的键组成。通过键来检索值的速度是非常快的,接近于 O(1),这是因为Dictionary<TKey,TValue>类是作为一个哈希表来实现的。检索速度取决于为 TKey 指定的类型的哈希算法的质量。
SortedDictionary<TKey, TValue>泛型类是检索运算复杂度为 O(log n) 的二叉搜索树,其中n是字典中的元素数。就这一点而言,它与SortedList<TKey, TValue>泛型类相似。这两个类具有相似的对象模型,并且都具有O(logn)的检索运算复杂度。这两个类的区别在于内存的使用以及插入和移除元素的速度:
SortedList<TKey, TValue>使用的内存比SortedDictionary<TKey, TValue>少。SortedDictionary<TKey, TValue>可对未排序的数据执行更快的插入和移除操作:它的时间复杂度为O(logn),而SortedList<TKey, TValue>为 O(n)。如果使用排序数据一次性填充列表,则SortedList<TKey, TValue>比SortedDictionary<TKey, TValue>快。
首先来看Dictionary的实现:
public class Dictionary<TKey,TValue>: IDictionary<TKey,TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback {
{
private struct Entry {
public int hashCode; // Lower 31 bits of hash code, -1 if unused
public int next; // Index of next entry, -1 if last
public TKey key; // Key of entry
public TValue value; // Value of entry
} private int[] buckets;
private Entry[] entries;
private IEqualityComparer<TKey> comparer; public Dictionary(int capacity): this(capacity, null) {} public Dictionary(IEqualityComparer<TKey> comparer): this(, comparer) {} public Dictionary(int capacity, IEqualityComparer<TKey> comparer) {
if (capacity < ) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
if (capacity > ) Initialize(capacity);
this.comparer = comparer ?? EqualityComparer<TKey>.Default;
} private void Initialize(int capacity) {
int size = HashHelpers.GetPrime(capacity);
buckets = new int[size];
for (int i = ; i < buckets.Length; i++) buckets[i] = -;
entries = new Entry[size];
freeList = -;
} public TValue this[TKey key] {
get {
int i = FindEntry(key);
if (i >= ) return entries[i].value;
ThrowHelper.ThrowKeyNotFoundException();
return default(TValue);
}
set {
Insert(key, value, false);
}
} private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
} if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= ; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -;
} private void Insert(TKey key, TValue value, bool add) { if( key == null ) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
} if (buckets == null) Initialize();
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length; for (int i = buckets[targetBucket]; i >= ; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
entries[i].value = value;
version++;
return;
}
}
int index;
if (freeCount > ) {
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
} entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
version++; if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
{
comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer(comparer);
Resize(entries.Length, true);
}
}
}
Dictionary<TKey,TValue>的数据成员转换为Entry结构,真正保存数据的是这里的Entry[] entries 数组,第一个元素小标为0,第二个为1......,但是查找和添加Dictionary<TKey,TValue>我们都是通过key来实现的,那么一个key究竟对应哪一个下标了,就需要这里的int[] buckets数组了。就如这里的FindEntry方法一样,首先获取key的哈希值获取buckets的下标(比如一个初始化为100个元素的字典,计算出来再buckets中的第50个元素),buckets 对应的值就是entries 数组的下标(buckets[50]=0,那么就应该取entries[0]的值了)。如果字典的元素个数是可以确定的话,那么建议指定capacity
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
现在我们来看看SortedList的实现:
public class SortedList<TKey, TValue> : IDictionary<TKey, TValue>, System.Collections.IDictionary, IReadOnlyDictionary<TKey, TValue>
{
private TKey[] keys;
private TValue[] values;
private IComparer<TKey> comparer;
public SortedList(int capacity) {
if (capacity < )
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired);
keys = new TKey[capacity];
values = new TValue[capacity];
comparer = Comparer<TKey>.Default;
} public SortedList(IDictionary<TKey, TValue> dictionary, IComparer<TKey> comparer)
: this((dictionary != null ? dictionary.Count : ), comparer) {
if (dictionary==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary); dictionary.Keys.CopyTo(keys, );
dictionary.Values.CopyTo(values, );
Array.Sort<TKey, TValue>(keys, values, comparer);
_size = dictionary.Count;
} public TValue this[TKey key] {
get {
int i = IndexOfKey(key);
if (i >= )
return values[i]; ThrowHelper.ThrowKeyNotFoundException();
return default(TValue);
}
set {
if (((Object) key) == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
int i = Array.BinarySearch<TKey>(keys, , _size, key, comparer);
if (i >= ) {
values[i] = value;
version++;
return;
}
Insert(~i, key, value);
}
} public int IndexOfKey(TKey key) {
if (key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
int ret = Array.BinarySearch<TKey>(keys, , _size, key, comparer);
return ret >= ? ret : -;
} private void Insert(int index, TKey key, TValue value) {
if (_size == keys.Length) EnsureCapacity(_size + );
if (index < _size) {
Array.Copy(keys, index, keys, index + , _size - index);
Array.Copy(values, index, values, index + , _size - index);
}
keys[index] = key;
values[index] = value;
_size++;
version++;
}
}
SortedList<TKey, TValue>的key和value分别存在TKey[] keys和TValue[] values数组里面,但是查找key用的不是哈希算法,而是二分查找 Array.BinarySearch<TKey>(keys, 0, _size, key, comparer),但是插入的时候却有
if (index < _size) {
Array.Copy(keys, index, keys, index + 1, _size - index);
Array.Copy(values, index, values, index + 1, _size - index);
}这样的code,意思就是如果SortedList里面已经有10个值,如果新插入的值应该是第一个, 那么需要把后面10个元素依次移动一个位置。移除元素也有类似的情况。
最后我们来看SortedDictionary的实现:
public class SortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>
{
public SortedDictionary(IDictionary<TKey,TValue> dictionary, IComparer<TKey> comparer) {
if( dictionary == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
} _set = new TreeSet<KeyValuePair<TKey, TValue>>(new KeyValuePairComparer(comparer)); foreach(KeyValuePair<TKey, TValue> pair in dictionary) {
_set.Add(pair);
}
} public SortedDictionary(IComparer<TKey> comparer) {
_set = new TreeSet<KeyValuePair<TKey, TValue>>(new KeyValuePairComparer(comparer));
} public TValue this[TKey key] {
get {
if ( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
} TreeSet<KeyValuePair<TKey, TValue>>.Node node = _set.FindNode(new KeyValuePair<TKey, TValue>(key, default(TValue)));
if ( node == null) {
ThrowHelper.ThrowKeyNotFoundException();
} return node.Item.Value;
}
set {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
} TreeSet<KeyValuePair<TKey, TValue>>.Node node = _set.FindNode(new KeyValuePair<TKey, TValue>(key, default(TValue)));
if ( node == null) {
_set.Add(new KeyValuePair<TKey, TValue>(key, value));
} else {
node.Item = new KeyValuePair<TKey, TValue>( node.Item.Key, value);
_set.UpdateVersion();
}
}
}
internal class TreeSet<T> : SortedSet<T> {} } public class SortedSet<T> : ISet<T>, ICollection<T>, ICollection, ISerializable, IDeserializationCallback, IReadOnlyCollection<T>
{
internal virtual Node FindNode(T item) {
Node current = root;
while (current != null) {
int order = comparer.Compare(item, current.Item);
if (order == ) {
return current;
} else {
current = (order < ) ? current.Left : current.Right;
}
} return null;
} public bool Add(T item) {
return AddIfNotPresent(item);
} internal virtual bool AddIfNotPresent(T item) {
if (root == null) { // empty tree
root = new Node(item, false);
count = ;
version++;
return true;
} //
// Search for a node at bottom to insert the new node.
// If we can guanratee the node we found is not a 4-node, it would be easy to do insertion.
// We split 4-nodes along the search path.
//
Node current = root;
Node parent = null;
Node grandParent = null;
Node greatGrandParent = null; //even if we don't actually add to the set, we may be altering its structure (by doing rotations
//and such). so update version to disable any enumerators/subsets working on it
version++; int order = ;
while (current != null) {
order = comparer.Compare(item, current.Item);
if (order == ) {
// We could have changed root node to red during the search process.
// We need to set it to black before we return.
root.IsRed = false;
return false;
} // split a 4-node into two 2-nodes
if (Is4Node(current)) {
Split4Node(current);
// We could have introduced two consecutive red nodes after split. Fix that by rotation.
if (IsRed(parent)) {
InsertionBalance(current, ref parent, grandParent, greatGrandParent);
}
}
greatGrandParent = grandParent;
grandParent = parent;
parent = current;
current = (order < ) ? current.Left : current.Right;
} Debug.Assert(parent != null, "Parent node cannot be null here!");
// ready to insert the new node
Node node = new Node(item);
if (order > ) {
parent.Right = node;
} else {
parent.Left = node;
} // the new node will be red, so we will need to adjust the colors if parent node is also red
if (parent.IsRed) {
InsertionBalance(node, ref parent, grandParent, greatGrandParent);
} // Root node is always black
root.IsRed = false;
++count;
return true;
} }
SortedDictionary的实现基本是靠TreeSet<T> (SortedSet<T>)来完成的,它的查找和添加都是在一个红黑树里面实现的。
Dictionary, SortedDictionary, SortedList 3个都有含类似IComparer<TKey> comparer的构造方法,Dictionary和SortedList 里面存储是用数组,所有它俩都有int capacity的指定,然而SortedDictionary依赖于树,所以没有该参数。所以Dictionary查找,插入、修改时间复杂度为O(1)(里面主要是哈希算法的时间,建议一个哈希桶里面存放一个元素),SortedList的查找时间复杂度为O(logn),但是插入和删除需要移动后面的元素,所以时间复杂 为O(n),SortedDictionary依赖于红黑树,所以查找、插入和修改 时间复杂度为O(logn)。
C# Dictionary, SortedDictionary, SortedList的更多相关文章
- C#数据结构汇总
对C#涉及到的数据结构做了一下简单的汇总,若有遗漏,欢迎补充~~ 还是以学习为目的,在此只是简单的介绍一下,希望对大家能有所帮助,能力有限为了不误导大家,不做详细深入的解析,还望见谅,非常欢迎大大们补 ...
- 常用数据结构及复杂度 array、LinkedList、List、Stack、Queue、Dictionary、SortedDictionary、HashSet、SortedSet
原文地址:http://www.cnblogs.com/gaochundong/p/data_structures_and_asymptotic_analysis.html 常用数据结构的时间复杂度 ...
- Dictionary(HashMap)的实现
什么是哈希表? 哈希表(Hash table,也叫散列表),是根据key而直接进行访问的数据结构.也就是说,它通过把key映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放 ...
- C#基础知识之Dictionary
最近使用了Dictionary,出现了意想不到的错误,先记录一下自己遇到的问题以及目前我的解决方法,然后温习一下Dictionary的基础用法. 一.自己遇到的问题 1.代码如下: namespace ...
- C# - 集合类
C#的集合类命名空间介绍: // 程序集 mscorlib.dll System.dll System.Core.dll // 命名空间 using System.Collections:集合的接口和 ...
- .NET面试题系列[11] - IEnumerable<T>的派生类
“你每次都选择合适的数据结构了吗?” - Jeffery Zhao .NET面试题系列目录 ICollection<T>继承IEnumerable<T>.在其基础上,增加了Ad ...
- C# 泛型简介
摘要:本文讨论泛型处理的问题空间.它们的实现方式.该编程模型的好处,以及独特的创新(例如,约束.一般方法和委托以及一般继承).此外,本文还讨论 .NET Framework 如何利用泛型. 下载 Ge ...
- 快速序列化组件MessagePack介绍
简介 MessagePack for C#(MessagePack-CSharp)是用于C#的极速MessagePack序列化程序,比MsgPack-Cli快10倍,与其他所有C#序列化程序相比,具有 ...
- 序列化 反序列化 MessagePack for C#
阅读目录 快速序列化组件MessagePack介绍 简介 使用 快速开始 分析器 内置的支持类型 对象序列化 DataContract兼容性 序列化不可变对象(序列化构造器) 序列化回调 Union ...
随机推荐
- Eclipse通过jdbc连接sqlserver2008数据库的两种方式
数据库登录身份验证方式有两种 其中服务器名称即为安装SQLServer2008的电脑,充当数据库服务器,在笔者这里就是自己的电脑名称. 身份验证方式有两种:windows身份验证和SQLSer ...
- java 标签编译后没有显示
1.原因现在还不知,试了几个地方可以和不可以 /** * @author feilong */ public class OverLoading { /**@param args put here c ...
- PHP替换指定字符串
在PHP中,有两个函数可以实现字符串替换,strtr()和str_repalce()函数. 首先我们简单了解下strtr()函数的定义及语法. strtr:转换指定字符. 两个语法: 第一种语法: s ...
- 欧拉函数,打表求欧拉函数poj3090
欧拉函数 φ(n) 定义:[1,N]中与N互质的数的个数 //互质与欧拉函数 /* 求欧拉函数 按欧拉函数计算公式,只要分解质因数即可 */ int phi(int n){ int ans=n; ;i ...
- hdu4605
两颗线段树,分别维护向左走向右走的情况 线段树的结点维护区间有多少点被路径经过了 离线读入所有询问,dfs遍历树的每一个结点,访问到v时解决对v的所有查询,在dfs过程中只需要维护根节点到v的链,线段 ...
- uva11610 树状数组+素数打表求因子,好题!
/* uva11610 树状数组+素数打表+离散化 打出的素数范围在2-1000000之间,不超过六位数,然后按照格式翻转成七位数 */ #include<bits/stdc++.h> u ...
- kafka删除topic数据
一.概述 生产环境中,有一个topic的数据量非常大.这些数据不是非常重要,需要定期清理. 要求:默认保持24小时,某些topic 需要保留2小时或者6小时 二.清除方式 主要有3个: 1. 基于时间 ...
- ***腾讯云直播(含微信小程序直播)研究资料汇总-原创
这段时间抽空研究了下直播技术,综合比较了下腾讯云直播的技术和文档方面最齐全,现把一些技术资料和文档归集如下: 1.微信小程序移动直播入门导读 https://cloud.tencent.com/doc ...
- 让我们了解 Ceph 分布式存储
前言 最近在学习 kubernetes 过程中,想实现 pod 数据的持久化.在调研的过程中,发现 ceph 在最近几年发展火热,也有很多案例落地企业.在选型方面,个人更加倾向于社区火热的项目,Glu ...
- 第三章XML简介
概念:XML:提供数据交换.系统配置.内容管理等的功能,可跨平台.跨网络.跨程序的数据描述方式.XSL:依靠XPath定位,提供显示模板,且专门为了显示XML文件信息的语言.CSS(层叠样式表):在网 ...