C# Hashtable/Dictionary写入和读取对比详解

 更新时间:2013年11月20日 15:22:34   作者:  
本文中将从基础角度讲解HashTable、Dictionary的构造和通过程序进行插入读取对比

一:HashTable
1.HashTable是一种散列表,他内部维护很多对Key-Value键值对,其还有一个类似索引的值叫做散列值(HashCode),它是根据GetHashCode方法对Key通过一定算法获取得到的,所有的查找操作定位操作都是基于散列值来实现找到对应的Key和Value值的。
2.我们需要使用一个算法让散列值对应HashTable的空间地址尽量不重复,这就是散列函数(GetHashCode)需要做的事。
3.当一个HashTable被占用一大半的时候我们通过计算散列值取得的地址值可能会重复指向同一地址,这就是哈希冲突。
在.Net中键值对在HashTable中的位置Position= (HashCode& 0x7FFFFFFF) % HashTable.Length,.net中是通过探测法解决哈希冲突的,当通过散列值取得的位置Postion以及被占用的时候,就会增加一个位移x值判断下一个位置Postion+x是否被占用,如果仍然被占用就继续往下位移x判断Position+2*x位置是否被占用,如果没有被占用则将值放入其中。当HashTable中的可用空间越来越小时,则获取得到可用空间的难度越来越大,消耗的时间就越多。
4.当前HashTable中的被占用空间达到一个百分比的时候就将该空间自动扩容,在.net中这个百分比是72%,也叫.net中HashTable的填充因子为0.72。例如有一个HashTable的空间大小是100,当它需要添加第73个值的时候将会扩容此HashTable.
5.这个自动扩容的大小是多少呢?答案是当前空间大小的两倍最接近的素数,例如当前HashTable所占空间为素数71,如果扩容,则扩容大小为素数131.

二:Dictionary

1.Dictionary是一种变种的HashTable,它采用一种分离链接散列表的数据结构来解决哈希冲突的问题。
2.分离链接散列表是当散列到同一个地址的值存为一个链表中。
3.这个变种HashTable的填充因子是1

三:本文将以代码的形式探索HashTable和Dictionary的插入和三种读取方式的效率(for/foreach/GetEnumerator)

复制代码 代码如下:

public class HashTableTest
    {
        static Hashtable _Hashtable;
        static Dictionary<string, object> _Dictionary;
        static void Main()
        {
            Compare(10);
            Compare(10000);
            Compare(5000000);
            Console.ReadLine();
        }
        public static void Compare(int dataCount)
        {
            Console.WriteLine("-------------------------------------------------\n");
            _Hashtable = new Hashtable();
            _Dictionary = new Dictionary<string, object>();
            Stopwatch stopWatch = new Stopwatch();
            //HashTable插入dataCount条数据需要时间
            stopWatch.Start();
            for (int i = 0; i < dataCount; i++)
            {
                _Hashtable.Add("Str" + i.ToString(), "Value");
            }
            stopWatch.Stop();
            Console.WriteLine(" HashTable插入" + dataCount + "条数据需要时间:" + stopWatch.Elapsed);

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            stopWatch.Start();
            for (int i = 0; i < dataCount; i++)
            {
                _Dictionary.Add("Str" + i.ToString(), "Value");
            }
            stopWatch.Stop();
            Console.WriteLine(" Dictionary插入" + dataCount + "条数据需要时间:" + stopWatch.Elapsed);

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            int si = 0;
            stopWatch.Start();
            for(int i=0;i<_Hashtable.Count;i++)
            {
                si++;
            }
            stopWatch.Stop();
            Console.WriteLine(" HashTable遍历时间:" + stopWatch.Elapsed + " ,遍历采用for方式");

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            si = 0;
            stopWatch.Start();
            foreach (var s in _Hashtable)
            {
                si++;
            }
            stopWatch.Stop();
            Console.WriteLine(" HashTable遍历时间:" + stopWatch.Elapsed + " ,遍历采用foreach方式");

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            si = 0;
            stopWatch.Start();
            IDictionaryEnumerator _hashEnum = _Hashtable.GetEnumerator();
            while (_hashEnum.MoveNext())
            {
                si++;
            }
            stopWatch.Stop();
            Console.WriteLine(" HashTable遍历时间:" + stopWatch.Elapsed + " ,遍历采用HashTable.GetEnumerator()方式");

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            si = 0;
            stopWatch.Start();
            for(int i=0;i<_Dictionary.Count;i++)
            {
                si++;
            }
            stopWatch.Stop();
            Console.WriteLine(" Dictionary遍历时间:" + stopWatch.Elapsed + " ,遍历采用for方式");

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            si = 0;
            stopWatch.Start();
            foreach (var s in _Dictionary)
            {
                si++;
            }
            stopWatch.Stop();
            Console.WriteLine(" Dictionary遍历时间:" + stopWatch.Elapsed + " ,遍历采用foreach方式");

            //Dictionary插入dataCount条数据需要时间
            stopWatch.Reset();
            si = 0;
            stopWatch.Start();
            _hashEnum = _Dictionary.GetEnumerator();
            while (_hashEnum.MoveNext())
            {
                si++;
            }
            stopWatch.Stop();
            Console.WriteLine(" Dictionary遍历时间:" + stopWatch.Elapsed + " ,遍历采用Dictionary.GetEnumerator()方式");


            Console.WriteLine("\n-------------------------------------------------");
        }
    }




四:从上面的结果可以看出

1.HashTable大数据量插入数据时需要花费比Dictionary大的多的时间。
2.for方式遍历HashTable和Dictionary速度最快。
3.在foreach方式遍历时Dictionary遍历速度更快。
五:在单线程的时候使用Dictionary更好一些,多线程的时候使用HashTable更好。
因为HashTable可以通过Hashtable tab = Hashtable.Synchronized(new Hashtable());获得线程安全的对象。
当然因为各自电脑的情况不一样,可能会有部分误差。如有问题,敬请斧正。

相关文章

  • C#实现跑马灯效果的示例代码

    C#实现跑马灯效果的示例代码

    跑马灯效果,功能效果大家应该都知道,就是当我们的文字过长,整个页面放不下的时候(一般用于公告等),可以让它自动实现来回滚动。本文将利用C#实现这一效果,感兴趣的可以了解一下
    2022-11-11
  • C#启动和停止windows服务的实例代码

    C#启动和停止windows服务的实例代码

    这篇文章介绍了C#启动和停止windows服务的实例代码,有需要的朋友可以参考一下
    2013-09-09
  • Winform项目中使用FastReport.Net报表控件

    Winform项目中使用FastReport.Net报表控件

    这篇文章介绍了Winform项目中使用FastReport.Net报表控件的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C#创建控制Windows服务

    C#创建控制Windows服务

    这篇文章介绍了C#创建和控制Windows服务的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02
  • C# 设计模式系列教程-桥接模式

    C# 设计模式系列教程-桥接模式

    桥接模式降低了沿着两个或多个维度扩展时的复杂度,防止类的过度膨胀,解除了两个或多个维度之间的耦合,使它们沿着各自方向变化而不互相影响。
    2016-06-06
  • C#线程 BeginInvoke和EndInvoke使用方法

    C#线程 BeginInvoke和EndInvoke使用方法

    本文开始C#线程系列讲座之一,即BeginInvoke和EndInvoke的使用方法,需要的朋友可以参考下
    2013-05-05
  • C#结合OpenCVSharp4使用直方图算法实现图片相似度比较

    C#结合OpenCVSharp4使用直方图算法实现图片相似度比较

    这篇文章主要为大家详细介绍了C#如何结合OpenCVSharp4使用直方图算法实现图片相似度比较,文中的示例代码简洁易懂,需要的小伙伴可以参考下
    2023-09-09
  • C#如何打开并读取usb的文件目录

    C#如何打开并读取usb的文件目录

    这篇文章主要介绍了用C#语言实现打开并读取usb的文件目录,但是实现此功能要注意一点必须在u盘插入才能接受到信息,需要的朋友可以参考下
    2015-07-07
  • C#调用Nero SDK刻录光盘的方法

    C#调用Nero SDK刻录光盘的方法

    这篇文章主要介绍了C#调用Nero SDK刻录光盘的方法,涉及C#调用NeroCOM组件实现光盘刻录的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • 基于c# 接口的实例详解

    基于c# 接口的实例详解

    本篇文章是对c#中的接口进行了详细的分析介绍,需要的朋友参考下
    2013-06-06

最新评论