首页 > 编程语言 >C#数据结构-Dictionary实现

C#数据结构-Dictionary实现

时间:2022-11-29 20:33:00浏览次数:35  
标签:return dictionary Dictionary C# int key new 数据结构 public


在看过一篇大神写的​​《带你看懂Dictionary的内部实现》​​,对Dictionary的内部实现有了一个清晰的了解,但纸上得来终觉浅,作为程序员,还是自己调试一下代码,记忆更深刻,为此专门拷贝出C#的Dictionary源码,因为源码有很多保护级别的代码,不能直接运行。下面贴出我调试没有问题的Dictionary源码(有部分代码没有重写),并附带官方调用实例,方便断点调试。

Dictionary源码:

using System;
using System.Collections;
using System.Collections.Generic;

namespace StructScript
{
/// <summary>
/// 哈希表的查找算法主要分为两步:
/// 第一步是用哈希函数将键转换为数组的一个索引,理想情况下不同的键都能转换为不同的索引值,但是实际上会有多个键哈希到到相同索引值上。
/// 因此,第二步就是处理碰撞冲突的过程。这里有两种处理碰撞冲突的方法:separate chaining(拉链法)和linear probing(线性探测法)。
/// </summary>

public class DictionaryScript<TKey, TValue> : IDictionary<TKey, TValue>
{
protected struct Entry
{
public int hashCode; //31位散列值,32最高位表示符号位,-1表示未使用
public int next; //下一项的索引值,-1表示结尾
public TKey key; //键
public TValue value; //值
}

protected int[] buckets;//处理hash碰撞,储存由键转换成的数组索引
protected Entry[] entries;//元素数组,用于维护哈希表中的数据
protected int count;//元素数量
protected int freeList;//空闲的列表
protected int freeCount;//空闲列表元素数量
protected IEqualityComparer<TKey> comparer;//哈希表中的比较函数
protected KeyCollection keys;//键集合
protected ValueCollection values;//值集合
protected const int MaxPrimeArrayLength = 0x7FEFFFFD;
//预设素数数组
protected static readonly int[] primes = {
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};

//调用本身的构造方法
public DictionaryScript() : this(0, null) { }

public DictionaryScript(int capacity) : this(capacity, null) { }

public DictionaryScript(int capacity, IEqualityComparer<TKey> comparer)
{
if (capacity < 0)
{
throw new ArgumentNullException();
}
if (capacity > 0)
{
Initialize(capacity);
}
this.comparer = comparer == null ? EqualityComparer<TKey>.Default : comparer;
}

/// <summary>
/// 需要一个大小为M的数组来储存键值对,那么需要一个能够将任意键转为该数组范围内的索引(0到M-1)的哈希函数
/// 这个哈希函数应该易于计算并且能够均匀分布所有的键。即对于任意键,0到M-1之间的每个整数都有相等可能性与之对应
/// 除留余数法。这个方法选择大小为素数M的数组,对于任意正整数k,计算k除以M的余数
/// 如果M不是素数的话,将不能有效利用键中所包含的所有信息,导致算法不能均匀地分布所有键。
/// </summary>
private void Initialize(int capacity)
{
//根据构造函数设定的初始容量,获取一个大于并接近的素数
int size = GetPrime(capacity);
buckets = new int[size];
for (int i = 0; i < buckets.Length; i++)
{
buckets[i] = -1;
}
entries = new Entry[size];
freeList = -1;
}

/// <summary>
/// 根据构造函数设定的初始容量,获取一个大于并接近的素数
/// </summary>
public int GetPrime(int min)
{
if (min < 0)
throw new ArgumentException();

for (int i = 0; i < primes.Length; i++)
{
int prime = primes[i];
if (prime >= min) return prime;
}

//如果超出预先的数组
for (int i = (min | 1); i < Int32.MaxValue; i += 2)
{
if (IsPrime(i) && ((i - 1) % 101 != 0))
return i;
}
return min;
}

/// <summary>
/// 是否是素数
/// </summary>
private bool IsPrime(int candidate)
{
if ((candidate & 1) != 0)
{
int limit = (int)Math.Sqrt(candidate);
for (int divisor = 3; divisor <= limit; divisor += 2)
{
if ((candidate % divisor) == 0)
return false;
}
return true;
}
return (candidate == 2);
}

/// <summary>
/// 扩容
/// </summary>
private int ExpandPrime(int oldSize)
{
int newSize = 2 * oldSize;
if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
return MaxPrimeArrayLength;
}
return GetPrime(newSize);
}

public TValue this[TKey key]
{
get
{
int i = FindEntry(key);
if (i >= 0)
{
return entries[i].value;
}
else
{
throw new KeyNotFoundException();
}
}
set
{
Insert(key, value, false);
}
}

public int Count
{
get
{
return count;
}
}

public KeyCollection Keys
{
get
{
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}

public ValueCollection Values
{
get
{
if (values == null) values = new ValueCollection(this);
return values;
}
}

IEnumerable<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
if (keys == null) keys = new KeyCollection(this);
return keys;
}
}

IEnumerable<TValue> IDictionary<TKey, TValue>.Values
{
get
{
if (values == null) values = new ValueCollection(this);
return values;
}
}

public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}

public void Add(TKey key, TValue value)
{
Insert(key, value, true);
}

private void Insert(TKey key, TValue value, bool add)
{
//key不能为空,value可以为空
if (key == null)
{
throw new ArgumentNullException();
}
if (buckets == null)
{
Initialize(0);
}
int collisionCount = 0;
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
//将HashCode的返回值转化为数组索引
int bucketIndex = hashCode % buckets.Length;
// 处理hash碰撞冲突
// 如果转换出的bucketIndex大于等于0,判断buckets数组中有没有相等的,如果相等,需要处理冲突
for (int i = buckets[bucketIndex]; i >= 0; i = entries[i].next)
{
//如果转换的hash值与之前已经添加的hash值相等,同时插入的key与之前的相同,处理冲突,key是唯一的,不能重复
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
{
if (add)
{
throw new ArgumentException();
}
entries[i].value = value;
return;
}
collisionCount++;
}
//数组索引
int index;
//如果空链表的长度大于0,FreeList链表不为空,因此字典会优先把新增元素添加到FreeList链表所指向的位置,添加后FreeCount减1
if (freeCount > 0)
{
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else
{
//如果数组已满,需扩容
if (count == entries.Length)
{
Resize();
bucketIndex = hashCode % buckets.Length;
}
index = count;
count++;
}
entries[index].hashCode = hashCode;
//新增元素的next指向上一个元素的索引
entries[index].next = buckets[bucketIndex];
entries[index].key = key;
entries[index].value = value;
//记录新增元素的索引
buckets[bucketIndex] = index;

// 冲突数达到了一定的阈值,之后更新Hash值
if (collisionCount > 100 && IsEqualityComparer(comparer))
{
comparer = EqualityComparer<TKey>.Default;
Resize(entries.Length, true);
}
}

private bool IsEqualityComparer(object comparer)
{
return (comparer == null || comparer == EqualityComparer<string>.Default);
}

/// <summary>
/// 扩容数组
/// </summary>
private void Resize()
{
Resize(ExpandPrime(count), false);
}

private void Resize(int newSize, bool forceNewHashCodes)
{
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++)
{
newBuckets[i] = -1;
}
Entry[] newEntries = new Entry[newSize];
Array.Copy(entries, 0, newEntries, 0, count);
if (forceNewHashCodes)
{
for (int i = 0; i < count; i++)
{
if (newEntries[i].hashCode != -1)
{
newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF);
}
}
}
for (int i = 0; i < count; i++)
{
if (newEntries[i].hashCode >= 0)
{
int bucket = newEntries[i].hashCode % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
buckets = newBuckets;
entries = newEntries;
}

public void Clear()
{
if (count > 0)
{
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
Array.Clear(entries, 0, count);
freeList = -1;
count = 0;
freeCount = 0;
}
}

public bool Contains(KeyValuePair<TKey, TValue> item)
{
return ContainsKey(item.Key);
}

public bool ContainsKey(TKey key)
{
return FindEntry(key) >= 0;
}

public bool ContainsValue(TValue value)
{
if (value == null)
{
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0 && entries[i].value == null) return true;
}
}
else
{
EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
for (int i = 0; i < count; i++)
{
if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true;
}
}
return false;
}

public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}

public bool Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}

public bool Remove(TKey key)
{
if (key == null)
{
throw new ArgumentNullException();
}
if (buckets != null)
{
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
{
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
{
//如果key在索引0,直接找到或者遍历到数组索引0找到key
if (last < 0)
{
// 把当前索引的bucket数组中的值重置,设置为-1
buckets[bucket] = entries[i].next;
}
else
{
//遍历数组时,找到key,把当前删除元素的下一个元素的索引赋值给当前删除元素的上一个元素的next
entries[last].next = entries[i].next;
}
entries[i].hashCode = -1;
entries[i].next = freeList;
entries[i].key = default(TKey);
entries[i].value = default(TValue);
freeList = i;
freeCount++;
return true;
}
}
}
return false;
}

public bool TryGetValue(TKey key, out TValue value)
{
int i = FindEntry(key);
if (i >= 0)
{
value = entries[i].value;
return true;
}
value = default(TValue);
return false;
}

private int FindEntry(TKey key)
{
if (key == null)
{
throw new ArgumentNullException();
}

if (buckets != null)
{
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next)
{
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}

public Enumerator GetEnumerator()
{
return new Enumerator(this, Enumerator.KeyValuePair);
}

IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return new Enumerator(this, Enumerator.KeyValuePair);
}

IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator(this, Enumerator.DictEntry);
}

public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{
private DictionaryScript<TKey, TValue> dictionary;
private int index;
private KeyValuePair<TKey, TValue> current;
private int getEnumeratorRetType;

internal const int DictEntry = 1;
internal const int KeyValuePair = 2;

internal Enumerator(DictionaryScript<TKey, TValue> dictionary, int getEnumeratorRetType)
{
this.dictionary = dictionary;
index = 0;
this.getEnumeratorRetType = getEnumeratorRetType;
current = new KeyValuePair<TKey, TValue>();
}

public bool MoveNext()
{
while ((uint)index < (uint)dictionary.count)
{
if (dictionary.entries[index].hashCode >= 0)
{
current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
index++;
return true;
}
index++;
}

index = dictionary.count + 1;
current = new KeyValuePair<TKey, TValue>();
return false;
}

public KeyValuePair<TKey, TValue> Current
{
get { return current; }
}

public void Dispose()
{
}

object IEnumerator.Current
{
get
{
if (getEnumeratorRetType == DictEntry)
{
return new DictionaryEntry(current.Key, current.Value);
}
else
{
return new KeyValuePair<TKey, TValue>(current.Key, current.Value);
}
}
}

void IEnumerator.Reset()
{
index = 0;
current = new KeyValuePair<TKey, TValue>();
}
}


public sealed class KeyCollection : ICollection<TKey>
{
private DictionaryScript<TKey, TValue> dictionary;

public KeyCollection(DictionaryScript<TKey, TValue> dictionary)
{
if (dictionary == null)
{
throw new ArgumentNullException();
}
this.dictionary = dictionary;
}

public KeyEnumerator GetEnumerator()
{
return new KeyEnumerator(dictionary);
}

IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator()
{
return new KeyEnumerator(dictionary);
}

IEnumerator IEnumerable.GetEnumerator()
{
return new KeyEnumerator(dictionary);
}

public int Count
{
get { return dictionary.Count; }
}

public void Add(TKey item)
{
throw new NotSupportedException();
}

public void Clear()
{
throw new NotSupportedException();
}

public bool Contains(TKey item)
{
return dictionary.ContainsKey(item);
}

public void CopyTo(TKey[] array, int arrayIndex)
{
throw new NotSupportedException();
}

public bool Remove(TKey item)
{
throw new NotSupportedException();
}
}

public struct KeyEnumerator : IEnumerator<TKey>, IEnumerator
{
private DictionaryScript<TKey, TValue> dictionary;
private int index;
private TKey currentKey;

internal KeyEnumerator(DictionaryScript<TKey, TValue> dictionary)
{
this.dictionary = dictionary;
index = 0;
currentKey = default(TKey);
}

public void Dispose()
{
}

public bool MoveNext()
{
while ((uint)index < (uint)dictionary.count)
{
if (dictionary.entries[index].hashCode >= 0)
{
currentKey = dictionary.entries[index].key;
index++;
return true;
}
index++;
}

index = dictionary.count + 1;
currentKey = default(TKey);
return false;
}

public TKey Current
{
get
{
return currentKey;
}
}

Object IEnumerator.Current
{
get
{
if (index <= 0 || (index > dictionary.count))
{
throw new IndexOutOfRangeException();
}

return currentKey;
}
}

void IEnumerator.Reset()
{
index = 0;
currentKey = default(TKey);
}
}


public sealed class ValueCollection : ICollection<TValue>
{
private DictionaryScript<TKey, TValue> dictionary;

public ValueCollection(DictionaryScript<TKey, TValue> dictionary)
{
if (dictionary == null)
{
throw new ArgumentNullException();
}
this.dictionary = dictionary;
}

public ValueEnumerator GetEnumerator()
{
return new ValueEnumerator(dictionary);
}

IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator()
{
return new ValueEnumerator(dictionary);
}

IEnumerator IEnumerable.GetEnumerator()
{
return new ValueEnumerator(dictionary);
}

public int Count
{
get { return dictionary.Count; }
}

public void CopyTo(TValue[] array, int arrayIndex)
{
throw new NotSupportedException();
}

public void Add(TValue item)
{
throw new NotSupportedException();
}

public void Clear()
{
throw new NotSupportedException();
}

public bool Contains(TValue item)
{
return dictionary.ContainsValue(item);
}

public bool Remove(TValue item)
{
throw new NotSupportedException();
}
}

public struct ValueEnumerator : IEnumerator<TValue>, IEnumerator
{
private DictionaryScript<TKey, TValue> dictionary;
private int index;
private TValue currentValue;

internal ValueEnumerator(DictionaryScript<TKey, TValue> dictionary)
{
this.dictionary = dictionary;
index = 0;
currentValue = default(TValue);
}

public void Dispose()
{
}

public bool MoveNext()
{
while ((uint)index < (uint)dictionary.count)
{
if (dictionary.entries[index].hashCode >= 0)
{
currentValue = dictionary.entries[index].value;
index++;
return true;
}
index++;
}
index = dictionary.count + 1;
currentValue = default(TValue);
return false;
}

public TValue Current
{
get
{
return currentValue;
}
}

Object IEnumerator.Current
{
get
{
if (index <= 0 || (index > dictionary.count))
{
throw new IndexOutOfRangeException();
}

return currentValue;
}
}

void IEnumerator.Reset()
{
index = 0;
currentValue = default(TValue);
}
}
}
}

Dictionary官方调用实例:

using System;
using System.Collections.Generic;
using System.Collections;

namespace StructScript
{
public class TestDictionary
{
static void Main(string[] args)
{
DictionaryScript<int, string> testDic = new DictionaryScript<int, string>(6);
testDic.Add(4, "4");
//在容量为6的情况下,传入4和11的key,得到的hashcode都是一样的,这里就要处理hash碰撞的问题
testDic.Add(11, "11");

DictionaryScript<int, string> test1Dic = new DictionaryScript<int, string>(6);
test1Dic.Add(4, "4");
test1Dic.Add(10, "11");
test1Dic.Add(9, "11");
test1Dic.Add(8, "11");
test1Dic.Add(7, "11");
test1Dic.Add(6, "11");
test1Dic.Add(5, "11");
//根据构造函数设定的初始容量,获取一个大于并接近的素数7
//C#内部有一个素数数组,在DictionaryScript的初始化函数Initialize中有具体实现
//超出数组容量,需要扩容,这里有数组扩容操作
test1Dic.Add(3, "11");
string value1;
test1Dic.TryGetValue(2, out value1);
test1Dic.Remove(3);


//下面是官方调用实例
DictionaryScript<string, string> openWith = new DictionaryScript<string, string>();

openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");

// The Add method throws an exception if the new key is
// already in the dictionary.
try
{
openWith.Add("txt", "winword.exe");
}
catch (ArgumentException)
{
Console.WriteLine("An element with Key = \"txt\" already exists.");
}

// The Item property is another name for the indexer, so you
// can omit its name when accessing elements.
Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);

// The indexer can be used to change the value associated
// with a key.
openWith["rtf"] = "winword.exe";
Console.WriteLine("For key = \"rtf\", value = {0}.",
openWith["rtf"]);

// If a key does not exist, setting the indexer for that key
// adds a new key/value pair.
openWith["doc"] = "winword.exe";

// The indexer throws an exception if the requested key is
// not in the dictionary.
try
{
Console.WriteLine("For key = \"tif\", value = {0}.",
openWith["tif"]);
}
catch (KeyNotFoundException)
{
Console.WriteLine("Key = \"tif\" is not found.");
}

// When a program often has to try keys that turn out not to
// be in the dictionary, TryGetValue can be a more efficient
// way to retrieve values.
string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
Console.WriteLine("Key = \"tif\" is not found.");
}

// ContainsKey can be used to test keys before inserting
// them.
if (!openWith.ContainsKey("ht"))
{
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("Value added for key = \"ht\": {0}",
openWith["ht"]);
}

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach (KeyValuePair<string, string> kvp in openWith)
{
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
DictionaryScript<string, string>.ValueCollection valueColl =
openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach (string s in valueColl)
{
Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
DictionaryScript<string, string>.KeyCollection keyColl =
openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach (string s in keyColl)
{
Console.WriteLine("Key = {0}", s);
}

// Use the Remove method to remove a key/value pair.
Console.WriteLine("\nRemove(\"doc\")");
openWith.Remove("doc");

if (!openWith.ContainsKey("doc"))
{
Console.WriteLine("Key \"doc\" is not found.");
}


Console.ReadLine();
}
}
}


标签:return,dictionary,Dictionary,C#,int,key,new,数据结构,public
From: https://blog.51cto.com/u_6871414/5897010

相关文章

  • C#数据结构-红黑树实现
    二叉查找树,他对于大多数情况下的查找和插入在效率上来说是没有问题的,但是他在最差的情况下效率比较低。红黑树保证在最坏的情况下插入和查找效率都能保证在对数的时间复杂度......
  • C#数据结构-有限状态机
    有限状态机FSM的要点是:  拥有一组状态,并且可以在这组状态之间进行切换。  状态机同时只能在一个状态。  一连串的输入或事件被发送给机器。  每个状态都......
  • 【最通俗易懂】A*寻路算法C#实现
    A*算法其实也不复杂,首先有以下几个概念:开启的节点表(OpenList)存放着所有的待检测的节点(坐标),每次都会从其中寻找出符合某个条件的节点。关闭的节点表(ClosedList)存放着所有......
  • 【最通俗易懂】C#有限状态机
    有限状态机表示有限个状态以及在这些状态之间的转换和动作等行为的数学模型。 有限状态机框架:Transitionenum:此枚举包含可由系统触发的转换标签。StateID枚举:这是游戏具......
  • win10 git报错:Unable to negotiate with port: no matching host key type found. The
    现象已经生成id_rsa的密钥,并且在git上进行了配置。但是用gitclone失败。报错:Unabletonegotiatewithport:nomatchinghostkeytypefound.Theiroffer:ssh-rsa。......
  • WGCLOUD - 如何实现监测mysql主从节点同步状态是否正常
    WGCLOUD的自定义监控项,可以执行一些我们自定义的指令或脚本,非常灵活本文我们尝试使用此功能来监测我们的mysql从节点是否在正常工作,如果如下两项值都为yes,那么slave节点是......
  • 如果摄像头不支持Web Socket,猿大师播放器还能在网页中播放RTSP流吗?
    问:我们的情况比较复杂,摄像头设备品牌和数量都比较多,分布在全国各地都有,地点分布比较广泛,有的甚至是比较老的型号,如果摄像头设备不支持WebSocket,猿大师播放器还可以在网页......
  • 3种CSS简单方法实现文字竖向排版
    下面介绍3种使用CSS实现文字竖向排版的方法:1、一个句子的竖向排列如图:<!DOCTYPEhtml><html><head><title>test</title><metacharset="UTF-8"></head>......
  • es6 class 实践
    ClassinES6从es6开始引入了class这个语法糖,针对babel,或者tsc,转码后,类会变成什么样,这篇文章将阐述编译后的结果。首先看看es5中的类的实现,举个栗子functionclassA()......
  • 【开发小技巧】028—使用CSS创建卡通动画加载效果
    在实际项目开发中,一般都会设计一个动画加载效果,今天这个加载效果非常有趣,可以帮助用户在等待程序加载时,缓解用户着急的情绪。HTML代码:在本文中,设计了代码的基本结构。<!DOCT......