HashTable 和 Dictionary<T,T>性能分析

最近学习相关集合知识时,发现微软对集合使用的建议,建议新的代码中使用泛型集合(Dictionary< T, T >,List< T >等),不要使用非泛型集合(HashTable,ArrayList等),原因有两点:非泛型集合容易出错(编译器不进行类型检查);非泛型集合性能较差,实践出真理,咱们对HashTable 和 Dictionary<T,T>集合性能进行验证。

微软建议非泛型集合不要再使用

DE0006: Non-generic collections shouldn’t be used

原因

在 .net 创建初始,泛型数据类型还不存在,这也是 System.Collections 命名空间中没有泛型类型的原因。后来,在 System.Collections.Generic 和 System.Collections.ObjectModel 命名空间中引入了泛型数据类型。

建议

对于新代码,你不应该使用非泛型集合,原因如下:

  1. 容易出错:由于非泛型集合是无类型的,它需要频繁地在对象(Object)和实际类型之间进行强制转换。由于编译器不能检查类型是否一致,所以更容易将错误的类型放入集合中。
  2. 性能较差:泛型集合的优点是值类型不必进行对象装箱。例如,List 将其数据存储在 int[] 数组中。这比将数据存储在 object[] 中要好得多,因为后者需要装箱。

非泛型集合替换泛型集合对照表

下表显示了如何将非泛型集合类型替换为 System.Collections.Generic 或System.Collections.ObjectModel 名称空间中的泛型类型:
在这里插入图片描述

代码验证

using System.Collections;
using System.Diagnostics;

//运行次数
int _runTimes = 10;
//集合元素个数
int _elementCont = 10000;

long[] _htAddRunTime = new long[_runTimes];
long[] _htGetRunTime = new long[_runTimes];
long[] _dictAddRunTime = new long[_runTimes];
long[] _dictGetRunTime = new long[_runTimes];

Stopwatch sw = new Stopwatch();

//ElapsedTicks 一秒钟tick数,一秒10000000次,即1毫秒10000次
//每个电脑不同
Console.WriteLine($"当前电脑Frequency:{Stopwatch.Frequency}");
Console.WriteLine($"一毫秒 = {Stopwatch.Frequency/1000} tick");
Console.WriteLine("");

for (int j = 0; j < _runTimes; ++j)
{
    sw.Restart();
    Hashtable _ht = new Hashtable();
    for (int i = 0; i < _elementCont; i++)
    {
        _ht.Add(i, i);
    }
    sw.Stop();
    _htAddRunTime[j] = sw.ElapsedTicks;

    sw.Restart();
    for (int i = 0; i < _elementCont; i++)
    {
        int temp = (int)_ht[i];
    }
    sw.Stop();
    _htGetRunTime[j] = sw.ElapsedTicks;


    sw.Restart();
    Dictionary<int, int> _dict = new Dictionary<int, int>();
    for (int i = 0; i < _elementCont; i++)
    {
        _dict.Add(i, i);
    }
    sw.Stop();
    _dictAddRunTime[j] = sw.ElapsedTicks;

    sw.Restart();
    for (int i = 0; i < _elementCont; i++)
    {
        int temp = _dict[i];
    }
    sw.Stop();
    _dictGetRunTime[j] = sw.ElapsedTicks;
}


const int _timeAlignment = 7;
const int _hintAlignment = 13;

Console.WriteLine($"增加{_elementCont}个元素循环{_runTimes}次耗时情况如下:");
Console.Write($"{"Hashtable元素:",_hintAlignment}");
long _htTotalAddTime = 0;
foreach (long _time in _htAddRunTime)
{
    Console.Write($"{_time,_timeAlignment}"); 
     _htTotalAddTime += _time;
}
Console.WriteLine($"");

Console.Write($"{"Dictionary元素:",_hintAlignment}");
long _dictTotalAddTime = 0;
foreach (long _time in _dictAddRunTime)
{
    Console.Write($"{_time,_timeAlignment}");
    _dictTotalAddTime += _time;
}
Console.WriteLine($"");

Console.WriteLine($"");

Console.WriteLine($"Hashtable元素平均耗时:{_htTotalAddTime / _runTimes} ");
Console.WriteLine($"Dictionary元素平均耗时:{_dictTotalAddTime / _runTimes} ");



Console.WriteLine($"");
Console.WriteLine($"");
Console.WriteLine($"获取{_elementCont}个元素循环{_runTimes}次耗时情况如下:");
Console.Write($"{"Hashtable元素:",_hintAlignment}");
long _htTotalGetTime = 0;
foreach (long _time in _htGetRunTime)
{
    Console.Write($"{_time,_timeAlignment}");
    _htTotalGetTime += _time;
}
Console.WriteLine($"");



Console.Write($"{"Dictionary元素:",_hintAlignment}");
long _dictTotalGetTime = 0;
foreach (long _time in _dictGetRunTime)
{
    Console.Write($"{_time,_timeAlignment}");
    _dictTotalGetTime += _time;
}
Console.WriteLine($"");

Console.WriteLine($"");
Console.WriteLine($"Hashtable元素平均耗时:{_htTotalGetTime / _runTimes} ");
Console.WriteLine($"Dictionary元素平均耗时:{_dictTotalGetTime / _runTimes} ");

注意:
ElapsedTicks没有明确的单位,是由Stopwatch.Frequency确定的。Stopwatch.Frequency表示秒表的频率(一秒中滴答的次数)。Stopwatch.Frequency我认为与电脑时钟频率有关(未确认,但在不同电脑实验过,确实根据电脑配置不同,数值确实不一样)。

添加10个数值型元素,并且循环添加10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素1456123011811119
Dictionary元素4409530111111442

获取10个数值型元素,并且循环获取10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素151011000001
Dictionary元素1517101000000151

添加100个数值型元素,并且循环添加10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素15125324024232418252738
Dictionary元素174818461110101151110188

获取100个数值型元素,并且循环获取10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素398687713146811
Dictionary元素94932333332297

添加1000个数值型元素,并且循环添加10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素294172171175224174167181199180193
Dictionary元素173293801029592809086100255

获取1000个数值型元素,并且循环获取10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素7664646367596262638866
Dictionary元素1046181820181818181728121

添加10000个数值型元素,并且循环添加10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素1785381944542313901923194583380247352251
Dictionary元素27481082100184183864413183216667721717

获取10000个数值型元素,并且循环获取10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素9172339279252250249950750291814601095
Dictionary元素2208594185182180180180180349257449

添加100000个数值型元素,并且循环添加10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素7019528199618762844429173307574496865196440632732843019
Dictionary元素40772409548566173708723325626410105746841401117678

获取100000个数值型元素,并且循环获取10次,运行时间如下:

元素类型第1次第2次第3次第4次第5次第6次第7次第8次第9次第10次平均值
HashTable元素65087528629557169251398411272325138586292972427024819
Dictionary元素3113330070198018191876481532302485247713598124

总结

每次运行程序的第一次添加和获取元素,耗时都比较高(原因不明,不吝赐教),但是不影响咋们分析 HashTable 和 Dictionary<T,T>性能。

不管从横向比较,还是平均值比较,以及第一次的纵向比较,很容易得出以下结论:

  1. 在100个元素以下,对于数值型元素,HashTable性能明显优于Dictionary<T,T>。
  2. 在1000个元素左右,对于数值型元素,创建第一个对象时,HashTable性能优于Dictionary<T,T>。
  3. 在10000个元素以上,对于数值型元素,Dictionary<T,T>性能优于HashTable。
  4. 对应数值型元素,1000个元素是个分界线。

针对对象类型的元素就不在此文展示了,大家有兴趣可以改写以上代码,很容易实现,这里直接说结论:

  1. 1000个元素以下,对于Object类型元素,HashTable获取元素性能明显优于Dictionary<T,T>,添加元素性能不佳。
  2. 10000个元素以上,对于Object类型元素,Dictionary<T,T>获取元素性能优于HashTable。

当然,以上是研究性结论,对应现实程序代码意义不大,因为当元素的数据达到10000个时,才出现1-2毫秒的耗时,但是元素超过10000个时,Dictionary性能已超过HashTable,所以微软的建议,我认为完全可以采纳(哈哈)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ts16dmy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值