Dictionary

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
posted @ 2018-06-04 22:10  vvf  阅读(232)  评论(0)    收藏  举报