在本文中将从基础角度讲解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());获得线程安全的对象。

  当然因为各自电脑的情况不一样,可能会有部分误差。如有问题,敬请斧正。

【数据类型】Dictionary 与 ConcurrentDictionary 待续

Dictionary<TKey, TValue> 泛型类提供了从一组键到一组值的映射。通过键来检索值的速度是非常快的,接近于 O(1),这是因为 Dictionary<TKey, TValue> 类是作为一个哈希表来实现的。检索速度取决于为 TKey 指定的类型的哈希算法的质量。TValue可以是值类型,数组,类或其他。

线程安全:CSDN上说法:

 只要不修改该集合,Dictionary<TKey, TValue> 就可以同时支持多个阅读器。即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。当出现枚举与写访问互相争用这种极少发生的情况时,必须在整个枚举过程中锁定集合。若允许多个线程对集合执行读写操作,您必须实现自己的同步。

 读写并行的时候需要加锁,但是加上锁性能就会受影响。

ConcurrentDictionary<TKey, TValue> framework4出现的,可由多个线程同时访问,且线程安全。用法同Dictionary很多相同,但是多了一些方法。ConcurrentDictionary 属于System.Collections.Concurrent 命名空间按照CSDN上所说:System.Collections.Concurrent 命名空间提供多个线程安全集合类。当有多个线程并发访问集合时,应使用这些类代替 System.Collections 和 System.Collections.Generic 命名空间中的对应类型。

一 Dictionary使用

1)实例化 Dictionary<int, string> dic = new Dictionary<int, string>();

添加   dic.Add(1, "one");

提取: string a = dic[1];  //one

键不能重复,值可以重复。

排序: var dicNew=from d in dic order by d.key select d;

1、C#中Hashtable、Dictionary详解以及写入和读取对比的更多相关文章

  1. C#中Hashtable、Dictionary详解以及写入和读取对比

    转载:http://www.cnblogs.com/chengxingliang/archive/2013/04/15/3020428.html 在本文中将从基础角度讲解HashTable.Dicti ...

  2. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

  3. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  4. AngularJS select中ngOptions用法详解

    AngularJS select中ngOptions用法详解   一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...

  5. 【转载】C/C++中extern关键字详解

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...

  6. oracle中imp命令详解 .

    转自http://www.cnblogs.com/songdavid/articles/2435439.html oracle中imp命令详解 Oracle的导入实用程序(Import utility ...

  7. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  8. python中threading模块详解(一)

    python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...

  9. Android中mesure过程详解

    我们在编写layout的xml文件时会碰到layout_width和layout_height两个属性,对于这两个属性我们有三种选择:赋值成具体的数值,match_parent或者wrap_conte ...

随机推荐

  1. MySQL中事务的分类

    从事务理论的角度来看,可以把事务分为以下几种类型 扁平事务(Flat Transactions) 带有保存点的扁平事务(Flat Transactions with Savepoints) 链事务(C ...

  2. python面试题(四)

    1 常用字符串格式化哪几种? 最方便的 print('hello %s and %s' % ('df', 'another df')) 复制代码 但是,有时候,我们有很多的参数要进行格式化,这个时候, ...

  3. nginx反向代理三台web

    1.首先我们需要在服务器中三个不同名字,并将他们赋值 2.切换到nginx—conf  把三台机器的nginx的配置文件分别命名为web1.conf.web2.conf.web3.conf vim的赋 ...

  4. 001-Java®语言规范、Java平台标准版文档、JVM概述

    一.概述 相关api地址:JDK10   JDK 9   JDK 8   JDK 7   JDK 6 Java语言和虚拟机规范: https://docs.oracle.com/javase/spec ...

  5. Armijo-Goldstein准则与Wolfe-Powell准则

    Armijo-Goldstein准则与Wolfe-Powell准则是不精确的一维搜索的两大准则. 之所以要遵循这些准则是为了能使算法收敛(求最优解).即要使我们的不精确的一维搜索的步长满足一定的规则, ...

  6. Vim 指令一览表

    vim 程序编辑器 移动光标的方法 h 或 向左箭头键(←) 光标向左移动一个字符 j 或 向下箭头键(↓) 光标向下移动一个字符 k 或 向上箭头键(↑) 光标向上移动一个字符 l 或 向右箭头键( ...

  7. nodejs socket server 强制关闭客户端连接

    nodejs socket server 强制关闭客户端连接: client.destroy()

  8. Pacemaker详解

    一.前言 云计算与集群系统密不可分,作为分布式计算和集群计算的集大成者,云计算的基础设施必须通过集群进行管理控制,而作为拥有大量资源与节点的集群,必须具备一个强大的集群资源管理器(Cluster sy ...

  9. Openstak(M版)控制节点安装

    #############修改hosts文件 # controller10.0.0.11 controller# compute110.0.0.31 compute1# block110.0.0.41 ...

  10. Kattis - register 【水】

    题意 就是 有一堆容器,然后可以执行加的操作,每个容量是 2, 3, 5, 7, 11, 13, 17, 19 然后 有进位 比如第一个 容器,到2了,就会重置为0,然后 下一个容器+ 1, 但是要保 ...