【问题标题】:Why SortedList<TKey, TValue> doesn't use pointers for the values?为什么 SortedList<TKey, TValue> 不对值使用指针?
【发布时间】:2014-02-12 14:39:35
【问题描述】:

所以我查看了SortedList&lt;TKey, TValue&gt; 的实现和Add 的实现(调用Insert 如下所示)真的让我很惊讶。

Add 方法执行明显的二分搜索来确定 KVP 应该进入的索引,但 Insert 似乎 好像可以改进 显着(当然是在更大的范围内):

private void Insert(int index, TKey key, TValue value)
{
  if (this._size == this.keys.Length)
    this.EnsureCapacity(this._size + 1);
  if (index < this._size)
  {
    Array.Copy((Array) this.keys, index, (Array) this.keys, index + 1, this._size - index);
    Array.Copy((Array) this.values, index, (Array) this.values, index + 1, this._size - index);
  }
  this.keys[index] = key;
  this.values[index] = value;
  ++this._size;
  ++this.version;
}

如果我没看错,并且我保留在任何时候犯错的权利,这是一个O(2n) 操作。

在我看来,应该用指针来实现。 有点像LinkedList 与键中的值有关,但不是链接,因为它不支持随机访问。更何况 key 只是 linked 到它的值。 get 操作不会变慢,而 remove 也不会因为我们有指针而变慢,但现在 add 操作将改为 O(n)

有人能解释一下为什么这个决定会朝着这个方向发展吗?

【问题讨论】:

  • 为什么不通过编写自己的自定义 SortedList 来测试这两种方法。只有当您的版本更快时,您的问题才会有意义。
  • @L.B:你是在暗示我的大 O 可能是错的吗?在那我需要从字面上而不是理论上验证可扩展性?我不是在讽刺,只是在问。
  • @MichaelPerrenoud O(2n) 与 O(n) 相同。它们都表现出线性增长并且将平等地扩展。重要的是曲线的形状,而不是斜率。
  • 查看stackoverflow.com/questions/322715/… 以获得两者之间的 Big O 比较(ArrayList 使用与此实现相同的动态大小数组)。
  • @MichaelPerrenoud 2 是复杂性分析忽略的常数因素之一。恒定因素可能很重要(尽管大哦是表达这一点的完全错误的工具)。但是,各种其他因素都会影响常数因素,包括您是否使用链表作为值以及如何布置数据。您不能忽略大多数不变的因素并声称有所改进,然后您只取消了其中一个,而没有调查其他因素是如何变化的,以及这是否会影响性能改进。

标签: c# .net data-structures


【解决方案1】:

这应该不会让您感到惊讶,它在 SortedList 的 MSDN 文章中有详细记录:

SortedDictionary 对未排序的数据具有更快的插入和删除操作,O(logn) 相对于 SortedList 的 O(n)。

SortedDictionary 使用红黑树(即“指针”),SortedList 是一个数组。您可以根据您对集合的处理方式在两者之间进行选择。两者都是 O(logn) 的查找,但如果你经常迭代集合,那么你可以在 SortedList 方面领先很多。它更有效地使用 CPU 缓存。对现代机器产生巨大影响。

还要注意,将项目添加到集合中的效率在很大程度上取决于项目的排序方式。 SortedDictionary 真的 喜欢随机数据,因此不必重新平衡树的几率要大得多。对其进行排序会给它带来最坏情况的 O(n) 行为。 SortedList 真的喜欢排序的项目,添加 O(1)。

【讨论】:

  • SortedList 当然讨厌带有反向排序输入的数据,这给了它最坏的情况。
  • 好的,他们这样做是因为需要不同 类型的数据结构。当谈到这些类型的事情时,这真的让我大开眼界。它与提供解决方案有关,以考虑不同的数据、其当前形式、其用法(常规与罕见)以选择正确的数据结构。它是这样实现的,因为SortedDictionary 完全符合我的意思,而(现在显然)SortedList 的用法完全不同。汉斯,谢谢,我希望我没有在现在可能是一个基本问题上浪费你的时间。
【解决方案2】:

除了速度/大 O 复杂度之外,另一个非常重要的区别是内存开销。使用树 (SortedDictionary),每个键/值对的开销为 50-70 字节(大约在 F# 的 AVL 树上测量,x64 上有 1M &lt;int64,int64&gt; 项,但红色/黑色应该在该范围内),而 SortedList每对只占用 2 个字节。

重点是,具有值类型,例如&lt;int,int&gt;,SortedDictionary 可以“浪费”比有用的有效负载多几倍的内存,具有更快随机插入的单一隔离优势。在实践中,SortedList 中 CPU 缓存的优势非常明显(O(lon n) 查找中的常数),因此应该衡量每个特定用例的差异(查找/插入比率、插入模式、内存/速度要求) .

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-19
    • 2023-03-20
    • 2011-01-16
    • 1970-01-01
    • 2012-07-25
    相关资源
    最近更新 更多