关于Hashtable与Dictionary性能的讨论(转)

http://www.cnblogs.com/jhh0111/archive/2008/10/23/1318223.html

看了eaglet的《几种C#框架提供的数据结构对单值查找的效率比较》,发现作者对Hashtable与Dictionary<K, V>存在一些误解,抽点空,讲讲我对Hashtable与Dic的看法。

     我个人是觉得,无论什么时候,都应该使用Dictionary<K,V>,理由如下:
     1、Dic是类型安全的,这有助于我们写出更健壮更具可读性的代码,而且省却我们强制转化的麻烦。这个相信大家都明白。
     2、Dic是泛行的,当K或V是值类型时,其速度远远超过Hashtable。这个大家对值类型与引用类型有所了解的话也会明白。
     3、如果K和V都是引用类型,如eaglet所测,Hashtable比Dic更快,这里我要指出,eaglet所做的测试是有问题的。原因在于Hashtable与Dic采用的是不同的数据结构。eaglet的“Dictionary 由于在Hashtable基础上封装了一层”这个说法是不对的。

     具体我也不讲了,因为有人(Angel Lucifer)已经讲得很清楚了,引用如下:

http://www.cnblogs.com/lucifer1982/archive/2008/06/18/1224319.html
http://www.cnblogs.com/lucifer1982/archive/2008/07/03/1234431.html 

     Hashtable在指定capacity参数时,它并不只开出capacity个槽的内存空间,而是开出比 capacity / 0.72(默认装填因子) 大的最小素数个槽的空间;而Dic在指定capacity时,是开出 比capacity 大的最小素数个槽的空间。因此可以看到,楼主虽然都指定capacity为10万,而实际上Hashtable的槽的总数远远大于Dic的槽的总数,也就 是占用的内存远远大于Dic,因此,如此测试是不公平不公正的,如要公平公正的测试,则应该把Dic的capacity指定为 10万/0.72,请大家再测试其性能。

     下表是我测试的Insert的性能。(机器是老爷机了,跑的太慢了)

测试条件 HashTable Dictionary
字符串长度 16,未排序 93 56
字符串长度 16,已排序 113 86
字符串长度 128,未排序 140 106
字符串长度 128,已排序 202 169
字符串长度 1024,未排序 473 477
字符串长度 1024,已排序 581 619


     4、楼主的测试不包括扩容所占的开销,实际上,Dic的扩容开销远远小于Hashtable,而我们知道,扩容是极为消耗性能的。

     总上所述,我认为应该始终使用Dictionary<K, V>,即使要用Hashtable了,也可以用Dictionary<object, object>来替代。

测试代码:


using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace StringDictionaryPerformance
{
    
class Program
    
{
        
static Random _Rand = new Random();

        
static Hashtable _Hashtable;
        
static Dictionary<stringobject> _Dictionary;
        
static object _object;

        
static string GetRandString(int length)
        
{
            StringBuilder str 
= new StringBuilder();

            
for (int i = 0; i < length; i++)
            
{
                str.Append((
char)_Rand.Next(32128));
            }


            
return str.ToString();
        }


        
static List<string> GetTestStrings(int length, int number)
        
{
            List
<string> retVal = new List<string>(number);

            
for (int i = 0; i < number; i++)
            
{
                retVal.Add(GetRandString(length));
            }


            
return retVal;
        }


        
static void TestInsert(List<string> strings, bool sort)
        
{
            
if (sort)
            
{
                strings.Sort();
            }


            Console.WriteLine(
string.Format("TestInsert string length = {0} count of strings = {1} sort={2}",
                strings[
0].Length, strings.Count, sort));

            Stopwatch stopWatch 
= new Stopwatch();


            Console.WriteLine(
"Begin Hashtable");

            _Hashtable 
= new Hashtable(strings.Count);
            stopWatch.Reset();
            stopWatch.Start();

            
foreach (string item in strings)
            
{
                
if (!_Hashtable.ContainsKey(item))
                
{
                    _Hashtable.Add(item, _object);
                }

            }


            stopWatch.Stop();
            Console.WriteLine(
string.Format("ElapsedMilliseconds = {0} ms", stopWatch.ElapsedMilliseconds));


            Console.WriteLine(
"Begin Dictoinary");

            
int tempCount = (int)(strings.Count / 0.72);
            _Dictionary 
= new Dictionary<stringobject>(tempCount);
            stopWatch.Reset();
            stopWatch.Start();

            
foreach (string item in strings)
            
{
                
if (!_Dictionary.ContainsKey(item))
                
{
                    _Dictionary.Add(item, _object);
                }

            }


            stopWatch.Stop();

            Console.WriteLine(
string.Format("ElapsedMilliseconds = {0} ms", stopWatch.ElapsedMilliseconds));
        }


        
static void TestFind(List<string> strings, bool sort)
        
{
            Console.WriteLine(
string.Format("TestFind string length = {0} count of strings = {1} sort={2}",
                strings[
0].Length, strings.Count, sort));

            Stopwatch stopWatch 
= new Stopwatch();

            Console.WriteLine(
"Begin Hashtable");

            stopWatch.Reset();
            stopWatch.Start();

            
foreach (string item in strings)
            
{
                
if (_Hashtable[item] != _object)
                
{
                    Console.WriteLine(
"Error!");
                }

            }


            stopWatch.Stop();
            Console.WriteLine(
string.Format("ElapsedMilliseconds = {0} ms", stopWatch.ElapsedMilliseconds));

            Console.WriteLine(
"Begin Dictoinary");

            stopWatch.Reset();
            stopWatch.Start();

            
foreach (string item in strings)
            
{
                
if (_Dictionary[item] != _object)
                
{
                    Console.WriteLine(
"Error!");
                }

            }


            stopWatch.Stop();

            Console.WriteLine(
string.Format("ElapsedMilliseconds = {0} ms", stopWatch.ElapsedMilliseconds));
        }


        
static void Main(string[] args)
        
{
            List
<string> strings;
            
            strings 
= GetTestStrings(16100000);
            TestInsert(strings, 
false);
            TestFind(strings, 
false);
            TestInsert(strings, 
true);
            TestFind(strings, 
true);

            strings 
= GetTestStrings(128100000);
            TestInsert(strings, 
false);
            TestFind(strings, 
false);
            TestInsert(strings, 
true);
            TestFind(strings, 
true);

            strings 
= GetTestStrings(1024100000);
            TestInsert(strings, 
false);
            TestFind(strings, 
false);
            TestInsert(strings, 
true);
            TestFind(strings, 
true);
        }

    }

}
posted @ 2011-02-16 10:01  董雨  阅读(223)  评论(0编辑  收藏  举报