【问题标题】:what is the implementation and complexity of operations of C# collections?C# 集合的实现和操作的复杂性是什么?
【发布时间】:2011-09-24 16:56:20
【问题描述】:

我想缓存 10.000 多个键/值对(两个字符串)并开始考虑哪种 .NET(2.0,绑定到 MS Studio 2005 :( ) 结构最好。所有项目将一次性添加,然后将是对特定键的几百次查询。
我已经阅读了the other question 中引用的 MSDN 描述,但我仍然错过了一些关于各种集合的实现/操作复杂性的细节。 例如。在上述问题中,引用 MSDN 的话说,SortedList 是基于树的,而 SortedDictionary “具有相似的对象模型”但复杂度不同。 另一个问题:HashTable 和 Dictionary 是否以相同的方式实现?
对于HashTable,他们write:

如果 Count 小于 Hashtable 的容量,则此方法是 O(1) 操作。如果需要增加容量以容纳新元素,则此方法变为 O(n) 操作,其中 n 为 Count。

但是当容量增加时呢?每个“添加”?然后添加一系列键/值对将是二次复杂度。与 SortedList 相同。

没有提到 OrderedDictionary,这里没有提到实现/复杂性。

也许有人知道一些关于 .NET 集合实现的好文章?

【问题讨论】:

  • 我终于有时间查看下面 Joe Cheng 所暗示的源代码 - 再次感谢! - 源代码是最好的文档;)这是我的总结: - 哈希表:当扩大容量(通过因子 2)时,会发生重新散列 - 这需要花费 ca。当前容量,但仅出现 log n 次,因此总体 O(n) -- 字典:内部 Hashtable 对象 -- SortedList:键和值的两个数组,容量放大 2 倍,但无论如何通常插入涉及复制元素 - 所以 0(n^ 2)一共——SortedDictionary:基于一棵树,插入O(log n)

标签: data-structures c#-2.0


【解决方案1】:

哈希表的容量Count不同。

通常 容量 -- 可以存储的最大项目数,通常与底层哈希桶的数量有关 -- 当“增长”时翻倍需要,尽管这取决于实现。 Count 仅指实际存储的项目数,必须小于或等于容量,否则不相关。

由于呈指数增长的间隔(在O(n)、n = Count、调整大小之间),大多数哈希实现要求O(1)amortized 访问。这句话只是说:“嘿!它是摊销的,并不总是正确的!”。

编码愉快。

【讨论】:

  • 谢谢,我错过了有关增长因子的信息。
【解决方案2】:

如果您要添加那么多对,您可以/应该使用this Dictionary constructor 提前指定容量。那么每次添加和查找都是 O(1)。

如果你真的想看看这些类是如何实现的,可以看the Rotor source或者使用.NET Reflector看System.Collections(不确定后者的合法性)。

【讨论】:

  • 感谢提示:构造函数和查看源代码的机会 - 这是我所缺少的。
【解决方案3】:

HashTableDictionary 的实现方式相同。 DictionaryHashTable 的通用替换。

ListDictionary等集合的容量必须增加时,它会以一定的速度增长。对于List,费率为2.0,即容量翻倍。我不知道Dictionary 的确切费率,但它的工作方式相同。

对于List,容量增加的方式意味着一个项目平均被复制了 1.3 倍。由于该值在列表增长时保持不变,因此 Add 方法仍然是平均 O(1) 操作。

【讨论】:

  • 谢谢。字典的容量是多少? - 哈希表中的桶数?你是如何获得1.3的?如果列表的容量正好加倍,那么每个项目都应该被复制 1 次,因为重新分配。但无论如何 Add 仍然是 O(1)。
  • @MkL:字典的容量是它可以容纳多少项目,即内部用于存储项目的数组有多大。桶本身不作为对象存在,KeyValue 项有一个数组,同一个桶中下一项的索引有一个数组。数字 1.3 是项目复制方式的结果。在任何给定时间,33% 到 100% 的项目至少被复制一次,其中 1/3 至少被复制两次,其中 1/3 至少被复制三次,依此类推。
【解决方案4】:

字典是一种哈希表;我从不使用原始的 Hashtable,因为它只包含“对象”。不用担心增加容量时插入是O(N);当哈希表已满时,字典总是将容量翻倍,因此 平均(摊销)复杂度为 O(1)。

您几乎不应该使用 SortedList(它基本上是一个数组),因为每次插入或删除的复杂度为 O(N)(假设数据尚未排序。如果数据已排序,那么您将得到 O(1) ,但是如果数据已经排序,那么你仍然不需要使用 SortedList,因为普通的 List 就足够了。)使用 SortedDictionary 代替 SortedList,它为插入、删除和搜索提供 O(N log N) .但是,SortedDictionary 比 Dictionary 慢,因此请仅在需要对数据进行排序时使用它。

您说您要缓存 10,000 个键值对。如果您想在进行任何查询之前进行所有插入,一个有效的方法是创建一个未排序的 List,然后 Sort 它,并使用 BinarySearch 进行查询。与使用 SortedDictionary 相比,这种方法节省了大量内存,并且为垃圾收集器创建的工作量更少。

【讨论】:

  • 感谢您的信息。为什么 SortedDictionary 比 Dictionary 慢?它是基于树的吗?与列表/权利的想法。并且 List 初始化为容量 = 键/值对的数量,正如 Joe Cheng 在下面暗示的那样。
  • @MkL SortedDictionary 和 SortedList 都不使用哈希码/哈希算法——这就是为什么它们是 O(lg n)/O(n lg n)/O(n) 用于访问/插入。 (我个人认为选择的名称很糟糕 :-) Dictionary 使用散列算法(尽管这不是 IDictionary 接口的要求)。至于速度 - 它可能会或可能不会更快。 Big-O 谈论限制。 NC 的特定值需要考虑到现实世界的性能
  • SortedDictionary 不仅理论上更慢,我还对其进行了基准测试:codeproject.com/KB/cross-platform/BenchmarkCppVsDotNet.aspx
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-13
  • 1970-01-01
相关资源
最近更新 更多