所以我正在查看SortedList<TKey, TValue>
的实施情况以及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
与密钥的值相关,但不是链接,因为它不支持随机访问。更多是键只是链接到它的值。 get 操作不会慢,而且删除也不会因为我们有指针,但 add 操作现在会改为O(n)
。< / p>
有人可以说明为什么这个决定可能会朝着这个方向发展吗?
答案 0 :(得分:10)
这不应该让您感到惊讶,它在SortdList的MSDN文章中有详细记载:
SortedDictionary对未排序数据的插入和删除操作更快,O(logn)而不是SortedList的O(n)。
SortedDictionary使用红黑树(即“指针”),SortedList是一个数组。您可以根据对集合的处理方式在两者之间进行选择。两者都是用于查找的O(logn),但是如果你经常迭代这个集合,那么你可以在SortedList上取得很大进展。它更有效地使用cpu缓存。在现代机器上产生巨大的变化。
另请注意,向集合添加项目的效率在很大程度上取决于项目的排序方式。一个SortedDictionary 真的喜欢随机数据,给它更好的几率,不必重新平衡树。对它进行排序会给出最坏情况下的O(n)行为。 SortedList 真的喜欢排序的项目,使得添加O(1)。
答案 1 :(得分:1)
除了速度/大O复杂度之外的另一个非常重要的区别是内存开销。使用树(SortedDictionary),每个键/值对的开销大小为50-70字节(大约在F#的AVL树上测量,x64上有1M <int64,int64>
项,但红色/黑色应该在该范围内),而SortedList每对只需要2个字节。
关键是,对于价值类型,例如<int,int>
,SortedDictionary可以“浪费”多倍的内存而不是有用的有效负载,具有更快的随机插入的单一隔离优势。实际上,SortedList中CPU缓存的优势非常明显(O(lon n)查找中的常量),应该测量每个特定用例的差异(查找/插入的比率,插入模式,内存/速度要求)