Dictionary和hashtable用法有点相似,他们都是基于键值对的数据集合,但实际上他们内部的实现原理有很大的差异,

先简要概述一下他们主要的区别,稍后在分析Dictionary内部实现的大概原理。

区别:1,Dictionary支持泛型,而Hashtable不支持。

        2,Dictionary没有装填因子(Load Facto)概念,当容量不够时才扩容(扩容跟Hashtable一样,也是两倍于当前容量最小素数),Hashtable是“已装载元素”与”bucket数组长度“大于装载因子时扩容。

        3,Dictionary内部的存储value的数组按先后插入的顺序排序,Hashtable不是。

       4,当不发生碰撞时,查找Dictionary需要进行两次索引定位,Hashtable需一次,。

 Dictionary采用除法散列法来计算存储地址,想详细了解的可以百度一下,简单来说就是其内部有两个数组:buckets数组和entries数组(entries是一个Entry结构数组),entries有一个next用来模拟链表,该字段存储一个int值,指向下一个存储地址(实际就是bukets数组的索引),当没有发生碰撞时,该字段为-1,发生了碰撞则存储一个int值,该值指向bukets数组.

从内部剖析C# 集合之--Dictionary从内部剖析C# 集合之--Dictionary

 

下面跟上次一样,按正常使用Dictionary时,看内部是如何实现的。

一,实例化一个Dictionary, Dictionary<string,string> dic=new Dictionary<string,string>();

    a,调用Dictionary默认无参构造函数。

    b,初始化Dictionary内部数组容器:buckets int[]和entries<T,V>[],分别分配长度3。(内部有一个素数数组:3,7,11,17....如图:从内部剖析C# 集合之--Dictionary);

  二,向dic添加一个值,dic.add("a","abc");

     a,将bucket数组和entries数组扩容3个长度。

     b,计算"a"的哈希值,

     c,然后与bucket数组长度(3)进行取模计算,假如结果为:2

     d,因为a是第一次写入,则自动将a的值赋值到entriys[0]的key,同理将"abc"赋值给entriys[0].value,将上面b步骤的哈希值赋值给entriys[0].hashCode,

       entriys[0].next 赋值为-1,hashCode赋值b步骤计算出来的哈希值。

    e,在bucket[2]存储0。

三,通过key获取对应的value,  var v=dic["a"];

   a, 先计算"a"的哈希值,假如结果为2,

   b,根据上一步骤结果,找到buckets数组索引为2上的值,假如该值为0.

   c, 找到到entriys数组上索引为0的key,

         1),如果该key值和输入的的“a”字符相同,则对应的value值就是需要查找的值。

         2) ,如果该key值和输入的"a"字符不相同,说明发生了碰撞,这时获取对应的next值,根据next值定位buckets数组(buckets[next]),然后获取对应buckets上存储的值在定位到entriys数组上,......,一直到找到为止。

         3),如果该key值和输入的"a"字符不相同并且对应的next值为-1,则说明Dictionary不包含字符“a”。

 

Dictionary里的其他方法就不说了,各位可以自己去看源码,下面来通过实验来对比Hashtable和Dictionary的添加和查找性能,

1,添加元素速度测评。

     循环5次,每次内部在循环10次取平均值,PS:代码中如有不公平的地方望各位指出,本人知错就改。

 a,值类型

static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine(string.Format("第{0}次执行:", i + 1));
                Add();
                Console.WriteLine("-------华丽的分分隔线---------");
            }
            
            Console.ReadKey();
        }
        public static void Add()
        {
            Hashtable ht = new Hashtable();
            Stopwatch st = new Stopwatch();

            long ticks1 = 0;
            for (int j = 0; j < 10; j++)
            {
                st.Reset();
                st.Start();
                for (int i = 0; i < 1000000; i++)
                {
                    ht.Add(i, i);
                }
                st.Stop();
                ticks1 += st.ElapsedTicks;
                ht.Clear();
            }

            Console.WriteLine(string.Format("Hashtable添加:{0}个元素,消耗:{1}", 1000000, ticks1 / 10));

            Dictionary<int, int> dic = new Dictionary<int, int>();
            ticks1 = 0;
            for (int j = 0; j < 10; j++)
            {
                st.Reset();
                st.Start();
                for (int i = 0; i < 1000000; i++)
                {
                    dic.Add(i, i);
                }
                st.Stop();
                ticks1 += st.ElapsedTicks;
                dic.Clear();
            }
            
            Console.WriteLine(string.Format("Dictionary添加:{0}个元素,消耗:{1}", 1000000, st.ElapsedTicks));
        }
View Code

相关文章:

  • 2021-10-26
  • 2021-04-27
  • 2021-11-17
  • 2022-12-23
  • 2021-07-05
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-11-15
  • 2021-09-26
  • 2022-12-23
  • 2021-07-04
  • 2022-01-24
相关资源
相似解决方案