这似乎与此question重复,后者询问“SortedList和SortedDictionary之间有什么区别?”不幸的是,答案只是引用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>
。
答案 0 :(得分:53)
我不确定MSDN文档在SortedList
和SortedDictionary
上的准确程度。似乎两者都是使用二叉搜索树实现的。但是,如果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>
不同,使用二叉树来插入和检索元素。它在插入时也有一些成本,因为有时树需要重新平衡(但不是每次插入)。
使用SortedList
或SortedDictionary
搜索元素时,性能非常相似,因为它们都使用二进制搜索。
在我看来,你应该从不使用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>
时间:
您希望在以下情况下更喜欢SortedDictionary<K, V>
:
编写代码
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)
性能差异的视觉表示。
答案 4 :(得分:2)
这就是它的全部。检索密钥具有可比性,但使用字典可以更快地添加。
我尝试尽可能多地使用SortedList,因为它允许我遍历键和值集合。据我所知,这对于SortedDictionary是不可能的。
我不确定这一点,但据我所知,字典在Tree结构中存储数据,而List存储数据在线性数组中。这就解释了为什么使用字典可以更快地插入和删除,因为必须转移更少的内存。它还解释了为什么你可以迭代SortedLists而不是SortedDictionary。
答案 5 :(得分:0)
对我们来说,一个重要的考虑因素是我们经常有小的字典(<100个元素),并且当前的进程在访问顺序内存时要快得多,而很少执行难以预测的分支。 (即遍历线性数组而不是遍历树) 因此,当字典中的元素少于60个时,SortedList <>在许多情况下通常是最快,内存效率最高的字典。