改善SortedDictionary的访问时间

时间:2010-11-15 12:07:22

标签: c# .net generics .net-3.5 c#-3.0

我在SortedDictionary<string, MyClass>

中有2百万件商品

我做了以下事情并花了很多年,有什么想法吗?

for(int i = 0; i<dic.Count-1; i++)
{
    Debug.WriteLine(dic.ElementAt(i).Value.ToString());
}

3 个答案:

答案 0 :(得分:4)

SortedDictionary<TKey, TValue>类不直接支持(快速)索引检索;它在内部实现为二叉搜索树。因此,每次调用LINQ Enumerable.ElementAt方法都会创建一个新的枚举器,它从头开始迭代集合中键值对(按键排序)表示的序列的每个值。 直到达到所需的索引。这意味着循环必须在它完成之前拉出类似1 + 2 + 3 + ... + Count(大约2万亿)的元素,使其(至少)时间复杂度呈二次方。

试试这个,它应该在大致线性的时间内运行:

foreach(var myClass in dic.Values)
   Debug.WriteLine(myClass);

如果您确实希望通过索引快速访问(从提供的代码中,似乎没有任何理由表明这一点),请考虑使用SortedList<TKey, TValue>。这个选择存在缺点(较慢的非附加插入和删除),你应该评估。

我还注意到循环条件是i < dic.Count - 1而不是更常见的i < dic.Count。这可能是一个错误的错误,或者您打算不考虑字典中的最后一个值。在后一种情况下,您可以维护一个局部变量作为计数器,或使用LINQ:

foreach(var myClass in dic.Values.Take(dic.Count - 1))
   Debug.WriteLine(myClass);

答案 1 :(得分:2)

foreach可能会更快,因为您不使用索引器

foreach (var value in dic.Values)
   Debug.Writeline(value)

另外,就速度而言,Debug.Writeline可能不是最佳选择(无论如何你要做200万个调试跟踪条目?)。考虑写入文件,数据库等

编辑查看反射器,在SortedDictionry中查找值可归结为二分搜索:

internal virtual Node<T> FindNode(T item)
{
    int num;
    for (Node<T> node = this.root; node != null; node = (num < 0) ? node.Left : node.Right)
    {
        num = this.comparer.Compare(item, node.Item);
        if (num == 0)
        {
            return node;
        }
    }
    return null;
}

SortedDictionary迭代的实现似乎更复杂:

public bool MoveNext()
{
    this.tree.VersionCheck();
    if (this.version != this.tree.version)
    {
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
    if (this.stack.Count == 0)
    {
        this.current = null;
        return false;
    }
    this.current = this.stack.Pop();
    SortedSet<T>.Node item = this.reverse ? this.current.Left : this.current.Right;
    SortedSet<T>.Node node2 = null;
    SortedSet<T>.Node node3 = null;
    while (item != null)
    {
        node2 = this.reverse ? item.Right : item.Left;
        node3 = this.reverse ? item.Left : item.Right;
        if (this.tree.IsWithinRange(item.Item))
        {
            this.stack.Push(item);
            item = node2;
        }
        else
        {
            if ((node3 == null) || !this.tree.IsWithinRange(node3.Item))
            {
                item = node2;
                continue;
            }
            item = node3;
        }
    }
    return true;
}

它似乎维护一个堆栈,其顶部元素是最小的(或最大的,取决于方向)one,因此始终是在迭代期间弹出和返回的堆栈。我没有进行任何复杂性分析,但每次运行二进制搜索时效率要高得多。

答案 2 :(得分:0)

使用foreach

    foreach (var pair in d)
        Debug.WriteLine(pair.Value);

我敢打赌,调试输出比字典查找需要更多时间。