.Net 3.5环境下常用数组性能测试

这件事情似乎很无聊,但是没人做,我来做下苦力吧。

以下是一些简单的测试。单位以ms计算。注意里面用到循环的数量有些事1W,有些是100W。

这些记录可以作为编程过程中的一些参考。

测试平台:

奔腾1.6G 双核CPU
1G内存
vs2008 调试环境测试。


一、ArrayList (100W,1W)

            Stopwatch timer = new Stopwatch();
            timer.Start();

            System.Collections.ArrayList al 
= new System.Collections.ArrayList();

            
for (int i = 0; i < 1000000; i++)
            {
                al.Add(i);
            }

            timer.Stop();

            Console.Write(timer.ElapsedMilliseconds.ToString() 
+ "\t");

            Stopwatch timer1 
= new Stopwatch();
            timer1.Start();

            
for (int i = 0; i < 10000; i++)
            {
                al.Contains(i);
            }

            timer1.Stop();

            Console.WriteLine(timer1.ElapsedMilliseconds.ToString());

98    775
160    891
107    773
193    769


二、Hashtable (100W,1W)

            Stopwatch timer = new Stopwatch();
            timer.Start();

            System.Collections.Hashtable ht 
= new System.Collections.Hashtable();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Add(i, i);
            }

            timer.Stop();

            Console.Write(timer.ElapsedMilliseconds.ToString() 
+ "\t");

            Stopwatch timer1 
= new Stopwatch();
            timer1.Start();

            
for (int i = 0; i < 10000; i++)
            {
                ht.ContainsKey(i);
            }

            timer1.Stop();

            Console.WriteLine(timer1.ElapsedMilliseconds.ToString());
        }

375 0
673 0
540 0
495 0

把timer1提高到100万(Hashtable (100W,100W))

389 139
616 277
516 140
610 277

三、HashSet (100W,100W)

Stopwatch timer = new Stopwatch();
            timer.Start();

            System.Collections.Generic.HashSet
<int> ht = new System.Collections.Generic.HashSet<int>();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Add(i);
            }

            timer.Stop();

            Console.Write(timer.ElapsedMilliseconds.ToString() 
+ "\t");

            Stopwatch timer1 
= new Stopwatch();
            timer1.Start();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Contains(i);
            }

            timer1.Stop();

            Console.WriteLine(timer1.ElapsedMilliseconds.ToString());



89 32
79 32
79 32
117 31

四、List (100W,1W)

            Stopwatch timer = new Stopwatch();
            timer.Start();

            System.Collections.Generic.List
<int> ht = new System.Collections.Generic.List<int>();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Add(i);
            }

            timer.Stop();

            Console.Write(timer.ElapsedMilliseconds.ToString() 
+ "\t");

            Stopwatch timer1 
= new Stopwatch();
            timer1.Start();

            
for (int i = 0; i < 10000; i++)
            {
                ht.Contains(i);
            }

            timer1.Stop();

            Console.WriteLine(timer1.ElapsedMilliseconds.ToString());


16 379
19 392
18 403
18 392

把List<int>换成List<object>

96 945
157 1033
106 909
193 910

换成string,i.ToString()

496 1238
531 1190
572 1246
536 1258

五、Dictionary (100W,100W)

            Stopwatch timer = new Stopwatch();
            timer.Start();

            System.Collections.Generic.Dictionary
<intint> ht = new System.Collections.Generic.Dictionary<intint>();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Add(i, i);
            }

            timer.Stop();

            Console.Write(timer.ElapsedMilliseconds.ToString() 
+ "\t");

            Stopwatch timer1 
= new Stopwatch();
            timer1.Start();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.ContainsKey(i);
            }

            timer1.Stop();

            Console.WriteLine(timer1.ElapsedMilliseconds.ToString());

113 35
125 34
124 34
126 34


六、Dictionary Linq查询 (100W,100W)

用个Linq试试
            Stopwatch timer = new Stopwatch();
            timer.Start();

            System.Collections.Generic.Dictionary
<intint> ht = new System.Collections.Generic.Dictionary<intint>();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Add(i, i);
            }

            timer.Stop();

            Console.Write(timer.ElapsedMilliseconds.ToString() 
+ "\t");

            Stopwatch timer1 
= new Stopwatch();
            timer1.Start();

            
for (int i = 0; i < 1000000; i++)
            {
                ht.Where(c 
=> c.Key == i);
            }

            timer1.Stop();

            Console.WriteLine(timer1.ElapsedMilliseconds.ToString());


112 177
107 78
125 70
107 82

结论:
1、如果是使用缓存的话,那么3.5带来的单泛型集合的HashSet可以替代List了。虽然载入速度慢一点,但是查询速度要比List泛型快很多。要注意到,上述测试List的查询时万级的,而HashSet是百万级

2、Dictionary泛型可以替换掉Hashtable了,虽然如果在字符或者object类型下可能会没这么明显。但是在数字类型的匹配上,Dictionary比Hashtable大概快了2倍,而这个开销估计是Hashtable的装箱造成的。

3、Linq还是要慢一些。

by yurow. http://www.cnblogs.com/birdshover/
posted @ 2008-03-19 23:31 Birdshover 阅读(3080) 评论(23)  编辑 收藏 网摘 所属分类: .Net language and other

  回复  引用  查看    
#1楼2008-03-19 23:42 | Tristan(Guozhijian)      
good works!
  回复  引用    
#2楼2008-03-19 23:44 | i am threem0126[未注册用户]
好文,找机会实用体验看看!
  回复  引用  查看    
#3楼2008-03-20 01:29 | yzlhccdec      
HashSet不能重复但是List可以
  回复  引用  查看    
#4楼[楼主]2008-03-20 02:03 | BirdsHover      
@yzlhccdec
大多数应用还是没有重复的吧

  回复  引用  查看    
#5楼2008-03-20 08:19 | 专研.NET      
好(本山语!)
  回复  引用  查看    
#6楼2008-03-20 08:38 | 戏水      
楼主是实干家。 这些数据很不错。
  回复  引用    
#7楼2008-03-20 08:51 | 韩[未注册用户]
牛的,我也待会测试一下
  回复  引用  查看    
#8楼2008-03-20 09:40 | StillWartersRunDeep      
lz 辛苦
  回复  引用    
#9楼2008-03-20 11:01 | A.Z![未注册用户]
你用linq干什么啊...
  回复  引用  查看    
#10楼[楼主]2008-03-20 11:04 | BirdsHover      
@A.Z!
用linq只是看下Linq对数组的操作效率,这里不够详细,有兴趣的朋友可以自己去做

  回复  引用    
#11楼2008-03-20 11:05 | diviner[未注册用户]
性能是一点一滴的,知此知彼,从小的做起.
这种比较很好.

  回复  引用    
#12楼2008-03-20 11:08 | diviner[未注册用户]
可以试试放引用类别的数据进行,再比较一下.
  回复  引用  查看    
#13楼2008-03-20 13:26 | Angel Lucifer      
1.HashSet<T>和List<T>是不同用途的数据结构,解决的场景也不尽相同,不存在谁被谁取代的问题。

比如List<T>可以排序,HashSet<T>可以吗?排序之后,List<T>的BinarySearch查询速度也相当的快速。
List<T>可以通过索引来查找数据,HashSet<T>可以吗?在知道其索引的情况下,List<T>的查找速度比HashSet<T>快得多,究其原因在于数组在内存的访问速度比其他数据结构都要快。

此外,如果仅在末端插入数据,List<T>的插入速度可以跟LinkedList<T>相媲美,比方说,如果你知道你大概要插入多少数据,完全可以预先分配内存,如楼主的情况则可以 new List<int>(1000000) ,这样就可以省去相当多的数组再分配,重新复制原数组内容的开销。当然,HashSet<T>也可以采取类似措施,但是比List<T>要考虑的方面多一些罢了。上面有位老兄也指出一个关键方面,就是List<T>的元素可以重复,而HashSet<T>却是不能重复的。

2.Dictionary<T>的确应该替代Hashtable,撇开泛型和装箱等原因,其内部采用的解决collision方案也要好于Hashtable。

采用何种数据结构,完全是看场景的需求如何,一概而论完全不足取。

  回复  引用    
#14楼2008-03-20 14:54 | George Shen[未注册用户]
1W的测试偏向于ArrayList,List<T>,因为顺序查找时前1W个更容易被找到,我相信要是List哪个测试改成:
for (int i = 0; i < 1000000; i++){ht.Contains(0);}的话速度肯定还是很快。可是如果是找
for (int i = 0; i < 10000; i++){ht.Contains(999999);}的话肯定很慢。

而对于采用Hash存储的情况,不同的Hash算法,对于不同的采样分布,效率肯定也是不同的,完全可以某些数据HashTable快,而Dictionary慢的(如果HashTable和Dictionary的Hash算法的确不同的话)

  回复  引用  查看    
#15楼2008-03-20 16:09 | Brilliance_DaveLin      
采用何种数据结构,完全是看场景的需求如何,一概而论完全不足取。
  回复  引用  查看    
#16楼[楼主]2008-03-20 18:01 | BirdsHover      
我最近要用的的应用是分词的,这里面的测试都是大数据量运用的。我测试了下,换成Dictionary和HashSet后,速度已经提高了好几倍,再摒弃了一些算法后,现在基本上速度是以前的30倍以上
  回复  引用  查看    
#17楼2008-03-21 13:49 | Jeffrey Zhao      
HashSet的查询自然要比List快,List又没有索引,Contains方法用的是遍历,O(N)的操作,HashSet,Dictionary用的是O(1)的操作。
至于LINQ慢,主要是一个Expression Tree的解析操作,如果可以避免的确应该不用。

  回复  引用  查看    
#18楼2008-03-22 10:47 | 毁于随      
@Jeffrey Zhao
Angel Lucifer说List可以用索引,老赵说没有索引,到底谁对?

  回复  引用  查看    
#19楼2008-03-22 11:52 | Angel Lucifer      
@毁于随

请注意措辞,呵呵。

List没有索引,它只是在内部维护一个数组而已。
但可以通过IndexOf等之类的方法得到它数组的索引,通过这个索引来访问数组。

要注意的是List允许有重复值,IndexOf查找到的索引只是查到该值在数组内第一次出现的位置。使用的时候要小心。知道索引之后,时间复杂度就成了O(1),比HashSet的O(1)要快的多。但是在之前,时间复杂度是O(N)。
所以说,要分场合。:-)

  回复  引用  查看    
#20楼2008-03-22 15:07 | 毁于随      
--引用--------------------------------------------------
Angel Lucifer: @毁于随

请注意措辞,呵呵。

List没有索引,它只是在内部维护一个数组而已。
但可以通过IndexOf等之类的方法得到它数组的索引,通过这个索引来访问数组。

要注意的是List允许有重复值,IndexOf查找到的索引只是查到该值在数组内第一次出现的位置。使用的时候要小心。知道索引之后,时间复杂度就成了O(1),比HashSet的O(1)要快的多。但是在之前,时间复杂度是O(N)。
所以说,要分场合。:-)
--------------------------------------------------------
知道索引以后就O(1)了?List不会自动维护这个吧?需要自己用变量记录?你上面说可以通过索引来查找数据,我还以为它在内部建立了索引,原来你的意思是可以通过IndexOf来操作.....晕了.

  回复  引用  查看    
#21楼2008-03-22 16:17 | Angel Lucifer      
@毁于随
知道索引之后,查找的时间复杂度不是O(1)是多少?

List内部只维护一个数组,所以它不会维护索引。如果需要索引值,只能自己计算。
HashSet内部则维护两个数组,此外还有一个链表。
通过Key的HashCode来查找,实际上就是计算内部数组索引的过程。

再次重申,采用何种数据结构,完全是看场景的需求如何,一概而论完全不足取。List和HashSet压根就不应该拿来作比较。

  回复  引用  查看    
#22楼2008-07-31 02:19 | JimLiu      
@BirdsHover
但是好像HashTable是线程安全的吧
HashSet是吗?
还有我觉得这个比较有些不妥,文章题目叫“常用数组性能测试”,但是却比较的是数组和哈希表间的性能。
从数据结构本身的角度来讲,顺序结构线性表——通常我们用数组来实现——特点是什么?随机访问,O(1)时间Append,O(n)时间插入、删除、查找。
而哈希表的特点是什么?散列(无序),O(1)时间追加、插入、删除、查找、(近似)随机访问。
二者是截然不同的,对于哈希表来说,它有键的概念,而对线性表仅有索引。
哈希表适合存储离散的数据,因为数据本身就是“散列”的。而线性表适合存储线性排列的数据。
数据的特性和应用的要求才决定了数据结构的选择,所以我个人觉得这样比较意义并不是很大。
总之我觉得数据结构一定要根据需求和特性相适应来正确的选择,否则如果我们需要查找,就算写了几百行代码,实现了斐波那契堆,实现了高效的插入和访问,却是一个根本无法查找的数据结构——和需求根本对不上号。
-----------------------------------------------------
@Angel Lucifer
老赵说的“索引”应该是说数据库里那种索引吧?
其实我觉得应该说List也是有索引的,下标就是元素的索引,只是在查找的时候我们是不知道索引的,所以这个索引只能做随机访问使用,对查找没有影响。
而哈希表的索引可以做散列运算,是O(1)时间,所以自然查找会快。

  回复  引用    
#23楼2008-08-29 14:21 | Rick66[未注册用户]
有没有哪位高人做过2.0和3.5综合性能的测试?



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1113949




相关文章:

相关链接: