1.基本成员
![]()
private struct Entry {
public int hashCode; //31位散列值,32最高位表示符号位,-1表示未使用
public int next; //下一项的索引值,-1表示结尾
public TKey key; //键
public TValue value; //值
}
private int[] buckets;//内部维护的数据地址
private Entry[] entries;//元素数组,用于维护哈希表中的数据
private int count;//元素数量
private int version;
private int freeList;//空闲的列表
private int freeCount;//空闲列表元素数量
private IEqualityComparer<TKey> comparer;//哈希表中的比较函数
private KeyCollection keys;//键集合
private ValueCollection values;//值集合
private Object _syncRoot;
buckets 就想在哈希函数与entries之间解耦的一层关系,哈希函数的F(k)变化不在直接影响到entries。
freeList 类似一个单链表,用于存储被释放出来的空间即空链表,一般有被优先存入数据。
freeCount 空链表的空位数量。
View Code
2.初始化函数
该函数用于,初始化的数据构造
![]()
private void Initialize(int capacity) {
//根据构造函数设定的初始容量,获取一个近似的素数
int size = HashHelpers.GetPrime(capacity);
buckets = new int[size];
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
entries = new Entry[size];
freeList = -1;
}
View Code
size 哈希表的长度是素数,可以使元素更均匀地分布在每个节点上。
buckets 中的节点值,-1表示空值。
freeList 为-1表示没有空链表。
buckets 和 freeList 所值指向的数据其实全是存储于一块连续的内存空间(entries )之中。
3.添加元素
![]()
private void Insert(TKey key, TValue value, bool add){
if( key == null ) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int targetBucket = hashCode % buckets.Length;
//循环冲突 查找是否存在该key (查找,删除时同样需要循环冲突链)
for (int i = buckets[targetBucket]; i >= 0; 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;
}
collisionCount++;
}
//添加元素
int index;
//是否有空列表 //优先添加到空列表
if (freeCount > 0) {
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;
//单链接的节点数(冲突数)达到了一定的阈值,之后更新散列值
if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer))
{
comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer(comparer);
Resize(entries.Length, true);
}
}
View Code
4.和Hashtable比较
相同点:
扩容 都是在一个素数数组中选择大于OldSize*2 的素数
不同点:
Dictionary 支持泛型
链地址法 解决冲突
loadfactor 1
Hashtable K/V 是object类型
双重散列法解决冲突 (说明:对于线性探测法,当聚焦问题严重或者表接近满时,要搜索一个关键字,往往要逐个检查很多个无关项(先于和搜索关键字匹配的元素插入)。为了解决聚焦问题,提出了双重散列算法,其基本策略和线性探测法一项,唯一不同是:它不是检查冲突位置后的每一个位置,而是采用另一个散列函数产生一个固定的增量。(跳跃式检查和插入,减小聚焦大小))
loadfactor 0.72
他们的结构不同
Dictrionry 使用int [] Bukets 将Entry 和hashcode 解耦 entry 中包括next指针 而Hashtable 没有next指针
5.
性能考虑,使用时评估容量减少rehash消耗性能
hashtable add值类型性能高于引用类型 是因为值类型gethashcode更快吗?
refer:http://www.cnblogs.com/lucifer1982/archive/2008/07/03/1234431.html