何时在SortedDictionary上使用SortedList <tkey,tvalue =“”> <tkey,tvalue =“”>?</tkey,> </tkey,>

时间:2009-09-04 02:36:00

标签: c# .net sortedlist sorteddictionary

这似乎与此question重复,后者询问“SortedListSortedDictionary之间有什么区别?”不幸的是,答案只是引用MSDN文档(它明确指出两者之间存在性能和内存使用差异),但实际上并没有回答这个问题。

事实上(根据MSDN,这个问题没有得到相同的答案):

  

SortedList<TKey, TValue>通用   class是二叉搜索树   O(log n)检索,其中n是   字典中的元素数量。   在这,它类似于   SortedDictionary<TKey, TValue>通用   类。这两个类有相似之处   对象模型,都有O(log n)   恢复。哪两个班   不同的是内存使用和速度   插入和移除:

     
      
  • SortedList<TKey, TValue>使用更少   记忆而不是SortedDictionary<TKey, TValue>

  •   
  • SortedDictionary<TKey, TValue>有   更快插入和移除   未排序数据的操作,O(log n)   而不是O(n)   SortedList<TKey, TValue>

  •   
  • 如果列表一次全部填充   从排序数据来看,SortedList<TKey, TValue>比快   SortedDictionary<TKey, TValue>

  •   

所以,显然这表明SortedList<TKey, TValue>是更好的选择除非您需要更快地插入和删除未排序数据的操作。

问题仍然存在,鉴于以上信息,使用SortedDictionary<TKey, TValue>的实际(现实世界,商业案例等)原因是什么?根据性能信息,这意味着根本不需要SortedDictionary<TKey, TValue>

6 个答案:

答案 0 :(得分:53)

我不确定MSDN文档在SortedListSortedDictionary上的准确程度。似乎两者都是使用二叉搜索树实现的。但是,如果SortedList使用二进制搜索树,为什么添加时的速度要慢于SortedDictionary

无论如何,这里有一些性能测试结果。

每个测试都在包含10,000个int32键的SortedList / SortedDictionary上运行。每个测试重复1.000次(Release build,Start without Debugging)。

第一组测试按顺序从0到9,999添加键。第二组测试添加0到9,999之间的随机混洗密钥(每个数字只添加一次)。

***** Tests.PerformanceTests.SortedTest

SortedDictionary Add sorted: 4411 ms
SortedDictionary Get sorted: 2374 ms


SortedList Add sorted: 1422 ms
SortedList Get sorted: 1843 ms

***** Tests.PerformanceTests.UnsortedTest

SortedDictionary Add unsorted: 4640 ms
SortedDictionary Get unsorted: 2903 ms


SortedList Add unsorted: 36559 ms
SortedList Get unsorted: 2243 ms

与任何剖析一样,重要的是相对表现,而不是实际数字。

如您所见,在排序数据上,排序列表比SortedDictionary更快。在未排序的数据上,SortedList在检索时会稍微快一点,但在添加时会慢大约9倍。

如果两者都在内部使用二进制树,那么对于SortedList,对未排序数据的添加操作要慢得多,这是非常令人惊讶的。排序列表也可能同时将项添加到排序的线性数据结构中,这会减慢它的速度。

但是,您希望SortedList的内存使用量等于或大于或等于SortedDictionary。但这与MSDN文档所说的相矛盾。

答案 1 :(得分:36)

我不知道为什么MSDN会说SortedList<TKey, TValue>使用二叉树来实现它,因为如果你看一下像Reflector这样的反编译器的代码,你会发现它不是真的。

SortedList<TKey, TValue>只是一个随时间增长的数组。

每次插入元素时,首先检查数组是否有足够的容量,如果没有,则重新创建一个更大的数组,并将旧元素复制到其中(如List<T>

之后,它使用二进制搜索搜索 where 以插入元素(这是可能的,因为该数组是可索引的并且已经排序)。

为了保持数组的排序,它会移动(或推动)位于要插入元素位置之后的所有元素(使用Array.Copy())。

例如:

// we want to insert "3" 

2  
4  <= 3
5
8
9
.      
.      
.  

// we have to move some elements first

2
.  <= 3
4 
5  |
8  v
9
.
.

这解释了为什么在插入未排序的元素时SortedList的性能如此糟糕。它几乎每次插入都必须重新复制一些元素。唯一没有做到的情况是必须在数组的末尾插入元素。

SortedDictionary<TKey, TValue>不同,使用二叉树来插入和检索元素。它在插入时也有一些成本,因为有时树需要重新平衡(但不是每次插入)。

使用SortedListSortedDictionary搜索元素时,性能非常相似,因为它们都使用二进制搜索。


在我看来,你应该从不使用SortedList来排序数组。除非您的元素非常少,否则将值插入列表(或数组)然后调用Sort()方法总是会更快。

当你有一个已经排序的值列表(例如:来自数据库)时,

SortedList非常有用,你想保持它的排序并执行一些利用它排序的操作(例如:{{1 Contains()的方法执行二分搜索而不是线性搜索

SortedList提供的优势与SortedDictionary相同,但如果要插入的值尚未排序,则效果会更好。


编辑:如果您使用的是.NET Framework 4.5,SortedList的替代方法是SortedDictionary<TKey, TValue>。它的工作方式与SortedSet<T>相同,使用二叉树,但键和值在这里相同。

答案 2 :(得分:10)

它们是出于两个不同目的吗?

.NET中这两种集合类型没有太大的语义差异。它们都提供键控查找以及按键的排序顺序保存条目。在大多数情况下,你可以选择其中任何一个。也许唯一的区别是索引检索SortedList许可。

但是表现?

然而,可能是一个更强的因素,可以在它们之间进行选择。这是它们渐近复杂性的表格视图。

+------------------+---------+----------+--------+----------+----------+---------+
| Collection       | Indexed | Keyed    | Value  | Addition |  Removal | Memory  |
|                  | lookup  | lookup   | lookup |          |          |         |
+------------------+---------+----------+--------+----------+----------+---------+
| SortedList       | O(1)    | O(log n) | O(n)   | O(n)*    | O(n)     | Lesser  |
| SortedDictionary | n/a     | O(log n) | O(n)   | O(log n) | O(log n) | Greater |
+------------------+---------+----------+--------+----------+----------+---------+

* Insertion is O(1) for data that are already in sort order, so that each 
  element is added to the end of the list (assuming no resize is required).

<强>摘要

粗略地总结一下,您需要SortedList<K, V>时间:

  1. 您需要索引查找。
  2. 希望减少内存开销。
  3. 您的输入数据已经排序(假设您已从db中订购)。
  4. 您希望在以下情况下更喜欢SortedDictionary<K, V>

    1. 相对整体性能很重要(关于缩放)。
    2. 您的输入数据无序。
    3. 编写代码

      SortedList<K, V>SortedDictionary<K, V>都会实现IDictionary<K, V>,因此在您的代码中,您可以从方法返回IDictionary<K, V>或将变量声明为IDictionary<K, V>。基本上隐藏实现细节,并针对接口进行编码。

      IDictionary<K, V> x = new SortedDictionary<K, V>(); //for eg. 
      

      将来,如果您对某个系列的性能特征不满意,则更容易切换。


      有关这两种集合类型的详细信息,请参阅原始的question链接。

答案 3 :(得分:3)

性能差异的视觉表示。

enter image description here

答案 4 :(得分:2)

这就是它的全部。检索密钥具有可比性,但使用字典可以更快地添加。

我尝试尽可能多地使用SortedList,因为它允许我遍历键和值集合。据我所知,这对于SortedDictionary是不可能的。

我不确定这一点,但据我所知,字典在Tree结构中存储数据,而List存储数据在线性数组中。这就解释了为什么使用字典可以更快地插入和删除,因为必须转移更少的内存。它还解释了为什么你可以迭代SortedLists而不是SortedDictionary。

答案 5 :(得分:0)

对我们来说,一个重要的考虑因素是我们经常有小的字典(<100个元素),并且当前的进程在访问顺序内存时要快得多,而很少执行难以预测的分支。 (即遍历线性数组而不是遍历树) 因此,当字典中的元素少于60个时,SortedList <>在许多情况下通常是最快,内存效率最高的字典。