几种C#框架提供的数据结构对单值查找的效率比较

做分词组件时,有网友提出采用Hashtable 数据结构查找字符串效率较低,建议改为Dictionary,其理由是采用Hashtable 时Key值是object 会触发装箱和拆箱动作,一直对这种说法表示怀疑,因为我理解只有值类型和引用类型通过object 互转时才会发生装箱和查询,引用类型之间强制转换不应发生装箱和拆箱,而Dictionary 泛型实际上底层还是调用的Hashtable,所以效率怎么会比Hashtable 要高呢?今天决定对比较常用的4种数据结构做一个测试,以便以后做系统性能优化时做一个参考。

这四种数据结构分别是 Hashtable, Dictionary, SortedDictionary,SortedList。它们所代表的数据结构分别是 哈希表、哈希表、二叉索引树和二分查找。测试设计如下。以10万条数据为一组,按照每条记录的字符串长度不同分为3组,分别是16,128,1024,并且分别根据原数据是排序的还是不排序的两种情况进行插入和单值查找的测试。测试时间单位为毫秒。下面看测试结果

插入的测试结果 (所有结果都是插入10万次的总时间)

测试条件 HashTable Dictionary SortedDictionary SortedList
字符串长度 16,未排序 14 21 896 8009
字符串长度 16,已排序 25 35 990 671
字符串长度 128,未排序 30 52 868 8415
字符串长度 128,已排序 43 67 1053 666
字符串长度 1024,未排序 132 262 1269 8159
字符串长度 1024,已排序 158 277 1036 684

查询的测试结果 (所有结果都是查询10万次的总时间)

测试条件 HashTable Dictionary SortedDictionary SortedList
字符串长度 16,未排序 13 15 412 366
字符串长度 16,已排序 25 29 349 315
字符串长度 128,未排序 31 40 492 438
字符串长度 128,已排序 42 54 408 371
字符串长度 1024,未排序 130 202 934 894
字符串长度 1024,已排序 160 219 801 757

从测试结果上看,无论是插入和查询,Hashtable的效率是最高的 其次是Dictionary 这和我的预期基本是一致的。

采用哈希方式插入,时间主要消耗在对哈希冲突情况的处理上,但由于只是选择空闲的桶而没有另外进行内存分配,其消耗时间是有限的。这里需要说明的是在我的测试设计中已经将每种数据结构的初始值设置为10万。而Dictionary 由于在Hashtable基础上封装了一层,效率稍有下降。

采用二叉搜索树插入,时间消耗在对树的节点的维护以及查找上(因为每次插入前需要确定记录是否有重复,重复记录不插入)。

二分查找,由于底层是一个顺序数组,当顺序插入时,效率比二叉搜索树稍高,当随机插入时会要求不断调整元素在数组中的位置,这导致大量的大块内存的拷贝,所以这种情况下效率极低,而且随着元素个数的增加会成指数上升。

哈希查找的时间消耗主要还是在冲突情况的匹配上,哈希函数的计算一般是很快的。但这里有一点必须要注意到,就是.net string  类型的特殊性,由于相同的string 指向相同的地址,所以在string 进行两两比较时,只比较地址,而不是每个字符都去计算,这大大提高了比较的效率。

而其他两种方式则需要比较字符串的大小,而不是仅仅比较相等,这带来了非常大的开销。

结论

对以字符串为主键的单值查找的优先选择 Hashtable 或者 Dictionary. 个人觉得如果只注重效率的话, Hashtable 更好一些,特别是在字符串较长时其效率几乎比Dictionary 快将近一倍。

测试代码

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<string, int> _Dictionary;
        static SortedDictionary<string, int> _SortDictionary ;
        static SortedList<string, int> _SortedList ;

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

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

            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();

            for (int i = 0; i < strings.Count; i++)
            {
                if (_Hashtable[strings[i]] != null)
                {
                    _Hashtable.Add(strings[i], i);
                }
            }

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


            Console.WriteLine("Begin Dictoinary");

            _Dictionary = new Dictionary<string,int>(strings.Count);
            stopWatch.Reset();
            stopWatch.Start();

            for (int i = 0; i < strings.Count; i++)
            {
                if (!_Dictionary.ContainsKey(strings[i]))
                {
                    _Dictionary.Add(strings[i], i);
                }
            }

            stopWatch.Stop();

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

            Console.WriteLine("Begin SortDictionary");

            _SortDictionary = new SortedDictionary<string, int>();
            stopWatch.Reset();
            stopWatch.Start();

            for (int i = 0; i < strings.Count; i++)
            {
                if (!_SortDictionary.ContainsKey(strings[i]))
                {
                    _SortDictionary.Add(strings[i], i);
                }
            }

            stopWatch.Stop();

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

            Console.WriteLine("Begin SortedList");

            _SortedList = new SortedList<string, int>(strings.Count);
            stopWatch.Reset();
            stopWatch.Start();

            for (int i = 0; i < strings.Count; i++)
            {
                if (!_SortedList.ContainsKey(strings[i]))
                {
                    _SortedList.Add(strings[i], i);
                }
            }

            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");

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

            for (int i = 0; i < strings.Count; i++)
            {
                if (_Hashtable[strings[i]] != null)
                {
                    Console.WriteLine("Error!");
                }
            }

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

            Console.WriteLine("Begin Dictoinary");

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

            for (int i = 0; i < strings.Count; i++)
            {
                if (!_Dictionary.ContainsKey(strings[i]))
                {
                    Console.WriteLine("Error!");
                }
            }

            stopWatch.Stop();

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

            Console.WriteLine("Begin SortDictionary");

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

            for (int i = 0; i < strings.Count; i++)
            {
                if (!_SortDictionary.ContainsKey(strings[i]))
                {
                    Console.WriteLine("Error!");
                }

            }

            stopWatch.Stop();

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

            Console.WriteLine("Begin SortedList");

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

            for (int i = 0; i < strings.Count; i++)
            {
                if (!_SortedList.ContainsKey(strings[i]))
                {
                    Console.WriteLine("Error!");
                }
            }

            stopWatch.Stop();

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


        }

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

            strings = GetTestStrings(128, 100000);
            TestInsert(strings, false);
            TestFind(strings, false);
            TestInsert(strings, true);
            TestFind(strings, true);

            strings = GetTestStrings(1024, 100000);
            TestInsert(strings, false);
            TestFind(strings, false);
            TestInsert(strings, true);
            TestFind(strings, true);

        }
    }
}

时间: 2024-10-08 16:40:11

几种C#框架提供的数据结构对单值查找的效率比较的相关文章

两种软件测试框架——JUnit和NUnit

今天,我们来介绍两种软件测试框架——JUnit和NUnit. 一.JUnit: 在介绍JUnit之前,先来简单说一下Java类的测试.Java 类测试是 Java 应用开发的重要步骤.Java 类测试分为基本类测试.组合类测试和分布式组件测试.而Junit 工具主要针对 Java 基本类测试和组合类测试. Java基本类的特点是,类的成员变量类型.类方法的参数类型是 Java 基本类型.组合类的特点是,类的成员变量类型可以是非 Java 基本类型的类.类方法的参数类型可以是非 Java 基本类型

自己动手,实现一种类似List&lt;T&gt;的数据结构(二)之Underscore.js的前缘

前言: 首先,小匹夫要祝各位看官圣诞快乐,新年愉快-.上一篇文章<自己动手,实现一种类似List<T>的数据结构(一)> 介绍了一下不依靠List<T>实现的各种接口,仿造一个轻量级数据结构的过程.可能有的看官会有一些疑问,例如一些功能可以通过Linq提供的拓展来实现呀.此言不虚但也不全对,为了我们在工作中能方便的操作集合而提供的这些拓展方法(包括我们自己也可以构建的拓展方法),例如 Where,Any,Max,All...balalbala等等这些方法都是针对IEnu

几种测试框架

这次随笔主要是关于三种测试框架:Junit,Qunit,Nunit框架 一:Junit 框架 JUnit是一个java语言的单元测试框架,它是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架.Junit测试是由程序员所测试,属于白盒测试范畴.因为程序员知道自己所写的东西是什么体系结构以及具体内容. Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试了. JUnit是一个开放源代码的Java测试框架,用于编写和运行可重复的测试. 二:Qunit

一种分布式框架设计(三)

本文讨论在分布式框架中使用到的两个数据结构.为了实现高性能,这两个数据结构都是无锁的. 第一个数据结构存储的是客户端发过来的socket.由于我们的框架只有一个线程接受用户的请求,所以很容易对每一个socket创建一个unique number(稍候我们再来看unique number包含了哪些信息).框架中有一个线程专门来做清理工作,同时关闭没有返回给客户端的socket.最后框架中有多个线程去查询所需的socket,由于是根据unique number进行查找,那么它保证了每一个socket

Struts和SpringMVC两种MVC框架比较

基于Web的MVC framework在J2EE的世界内已是空前繁荣.TTS网站上几乎每隔一两个星期就会有新的MVC框架发布.目前比较好的MVC,老牌的有Struts.Webwork.新兴的MVC框架有Spring MVC.Tapestry.JSF等.这些大多是著名团队的作品,另外还有一些边缘团队的作品,也相当出色,如Dinamica.VRaptor等.这些框架都提供了较好的层次分隔能力.在实现良好的MVC 分隔的基础上,通过提供一些现成的辅助类库,同时也促进了生产效率的提高. 如何选择一个好的

未找到具有固定名称&ldquo;System.Data.SQLite&rdquo;的 ADO.NET 提供程序的实体框架提供程序

用户代码未处理 System.InvalidOperationException   HResult=-2146233079   Message=未找到具有固定名称"System.Data.SQLite"的 ADO.NET 提供程序的实体框架提供程序.请确保在应用程序配置文件的"entityFramework"节中注册了该提供程序.有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=260882.   Source=E

07-使用框架提供的自动建表(没有提供配置文件)

4.2  使用activiti框架自动建表功能 框架提供了和hibernate类似的自动建表功能. 搭建开发环境: 第一步:创建一个web(我们这个框架不是必须依赖tomcat这个容器才可以运行)/java项目activiti1110,如果它是一个struts框架,那就必须创建一个web项目(struts是表现层的一个框架,需要web容器提供支持) 可以使用例子项目的JAR包(不必吝啬) Build Path->Add to Build Path的效果是把JAR包加入构建路径里面,其实就是把JA

06-使用框架提供的建表语句创建数据库表

4.    初始化表结构 4.1  使用activiti框架提供的建表语句 * 第一步:获得建表语句 *  第二步:创建一个数据库 * 第三步:进入当前数据库中 4.2  使用activiti框架的自动建表功能  类似于我们学过的hibernate,它可以自动建表 hbm2ddl.auto = update

一种分布式框架设计(一)

通常,当服务涉及到的数据量大到一定程度以后,我们会考虑拆分数据.在这种分布式架构中,每个结点只拥有总数据量的其中一部分,而最终的输出结果会汇总所有结点的结果.这种Map-reduce思想的架构,是尽量不去查分程序,而只是拆分数据来支持大数据的处理,如下图所示.这种框架对每个worker结点的可靠性要求比较高,如果某一个worker结点挂掉了,那么最后的输出结果将是不全的. 我设计的这个分布式框架解决的是另一个问题.比如一个程序中含有A, B, C, D四个逻辑,它们在程序中是串行执行的,彼此之间