【问题标题】:When to use a SortedList<TKey, TValue> over a SortedDictionary<TKey, TValue>?何时在 SortedDictionary<TKey, TValue> 上使用 SortedList<TKey, TValue>?
【发布时间】:2009-09-04 02:35:59
【问题描述】:

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

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

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

  • SortedList&lt;TKey, TValue&gt; 使用较少 内存大于SortedDictionary<TKey, TValue>

  • SortedDictionary&lt;TKey, TValue&gt; 有 更快的插入和移除 未排序数据的操作,O(log n) 与 O(n) 相反 SortedList&lt;TKey, TValue&gt;

  • 如果列表被一次性填充 从排序数据来看,SortedList<TKey, TValue>SortedDictionary&lt;TKey, TValue&gt;

因此,显然这表明SortedList&lt;TKey, TValue&gt; 是更好的选择除非您需要对未排序数据进行更快的插入和删除操作。

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

【问题讨论】:

  • 请注意,您引用的部分几乎说明了一切。但是请注意,您关于“更快地插入和删除未排序数据的操作”的说法并不完全正确。它实际上是在说“插入和删除”操作在 SortedList 上总是具有更高的时间复杂度。关于“未排序数据”的陈述仅涉及通过它们的构造函数用数据初始化这些结构。
  • 这似乎与 .NET 2.0 相关。 SortedList 的实现似乎从 3.0 开始发生了变化。我最近需要自己回答这个问题,发现这个问题及其回答可能不再与 .NET 4.5 的用户相关。

标签: c# .net sortedlist sorteddictionary


【解决方案1】:

我不确定 SortedListSortedDictionary 上的 MSDN 文档有多准确。似乎是说两者都是使用二叉搜索树实现的。但是如果 SortedList 使用二叉搜索树,为什么它在加法上会比 SortedDictionary 慢得多?

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

每个测试都在包含 10,000 个 int32 键的 SortedList / SortedDictionary 上运行。每个测试重复 1,000 次(发布构建,开始而不调试)。

第一组测试按从 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 文档所说的相矛盾。

【讨论】:

  • 它们的复杂性界限将与使用数组的 SortedList 实现一致。然后将使用 O(log n) 中的二进制搜索执行查找。插入将在 O(n) 中。
  • 我要补充一点,SortedList 实际上在使用较小的列表时更快,即使在“未排序”的情况下,在我自己的测试中,阈值出现在大约 700 个元素左右。因此,经验法则是“除非您需要存储超过 1000 个元素,否则使用 SortedList”。
  • @gatopeich:你说的是检索速度还是插入速度?我希望阈值更像是 10 到 30 个元素,而不是插入场景中的 700 个。在任何情况下,向SortedList 添加(或删除)随机项目对于大型列表来说会变得非常缓慢,因此即使只有 1% 的机会遇到包含 10,000 个元素的列表,您也应该改用 SortedDictionary
【解决方案2】:

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

SortedList&lt;TKey, TValue&gt; 只是一个随时间增长的数组。

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

之后,它使用二分搜索搜索 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&lt;TKey, TValue&gt; 不同,它使用二叉树来插入和检索元素。它在插入时也有一些成本,因为有时需要重新平衡树(但不是每次插入)。

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


在我看来,您应该永远使用SortedList 来对数组进行排序。除非您的元素很少,否则将值插入列表(或数组)然后调用Sort() 方法总是更快。

SortedList 在您有一个已经排序的值列表(例如:来自数据库)时非常有用,您希望保持它的排序并执行一些可以利用它已排序的操作(例如:Contains() 的方法SortedList 执行二分查找而不是线性查找)

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


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

【讨论】:

  • newest version of the SortedList&lt;,&gt; doc 说:SortedList&lt;TKey, TValue&gt; 泛型类是一个键/值对数组 – 它还强调使用SortedList&lt;,&gt; 你可以做类似@ 987654346@,即像数组一样按整数索引。
  • 好吧,如果您阅读任何基本算法书籍,您就会意识到实现二叉树的一种方法是使用数组webdocs.cs.ualberta.ca/~holte/T26/tree-as-array.html
  • 我猜 tigrou 的意思是 SortedList 是一个数组实现,而 SortedDictionary 是一个 Linked 实现,这可以解释他在逆向工程代码中看到的内容以及 Ash 在测试中看到的内容
【解决方案3】:

它们是否有两种不同的用途?

这两种集合类型在 .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&lt;K, V&gt; 什么时候:

  1. 您需要索引查找。
  2. 最好减少内存开销。
  3. 您的输入数据已经排序(假设您已经从 db 中排序)。

您可能更喜欢SortedDictionary&lt;K, V&gt;,当:

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

编写代码

SortedList&lt;K, V&gt;SortedDictionary&lt;K, V&gt; 都实现了IDictionary&lt;K, V&gt;,因此在您的代码中,您可以从方法中返回IDictionary&lt;K, V&gt; 或将变量声明为IDictionary&lt;K, V&gt;。基本上隐藏实现细节,以及针对接口的代码。

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

将来,如果您对某个集合的性能特征不满意,可以更轻松地从其中一种切换。


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

【讨论】:

    【解决方案4】:

    性能差异的直观表示。

    【讨论】:

    • 这个视觉效果如何?
    • 我不得不用我的眼睛看到它:)
    【解决方案5】:

    仅此而已。检索键是可比的,但使用字典加法要快得多。

    我尝试尽可能多地使用 SortedList,因为它允许我遍历键和值集合。据我所知,SortedDictionary 无法做到这一点。

    对此我不确定,但据我所知,字典将数据存储在树结构中,而列表将数据存储在线性数组中。这就解释了为什么使用字典插入和删除要快得多,因为需要移动的内存更少。它还解释了为什么您可以迭代 SortedLists 而不能迭代 SortedDictionary。

    【讨论】:

    • SortedDictionary 具有要迭代的 KeysValues 集合。它唯一缺少的是对这两个集合的元素的索引访问,SortedList 确实允许。
    • 对不起,是的。你可以 foreach 它们,但我几乎从不使用 foreach 循环,这就是为什么我错误地认为它根本不可能。
    • “我不确定这一点,但据我所知,字典将数据存储在树结构中”这是不正确的。 .net 中的标准字典类使用数组。
    【解决方案6】:

    对我们来说一个重要的考虑因素是,我们通常有小的字典( 通常是许多用例中最快且内存效率最高的字典。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 2012-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      相关资源
      最近更新 更多