为什么检查字典是否包含密钥更快,而不是在不包含异常的情况下捕获异常?

时间:2013-04-19 09:47:17

标签: c# performance dictionary

想象一下代码:

public class obj
{
    // elided
}

public static Dictionary<string, obj> dict = new Dictionary<string, obj>();

方法1

public static obj FromDict1(string name)
{
    if (dict.ContainsKey(name))
    {
        return dict[name];
    }
    return null;
}

方法2

public static obj FromDict2(string name)
{
    try
    {
        return dict[name];
    }
    catch (KeyNotFoundException)
    {
        return null;
    }
}

我很好奇这两个函数的性能是否有差异,因为第一个函数应该比第二个函数更低 - 假设它需要检查两次,如果字典包含一个值,而第二个函数确实需要访问字典只有一次,但是WOW,实际上是相反的:

循环1 000 000个值(现有10万个,不存在90 000个):

  

第一个功能:306毫秒

     

第二个函数:20483毫秒

为什么?

编辑:正如您在下面的评论中注意到的那样,如果有0个非现有密钥,第二个函数的性能实际上略好于第一个函数。但是,一旦存在至少一个或多个非现有密钥,第二个密钥的性能会迅速下降。

2 个答案:

答案 0 :(得分:398)

一方面,throwing exceptions is inherently expensive,因为必须解开堆栈等 另一方面,通过键访问字典中的值很便宜,因为它是一种快速的O(1)操作。

BTW:正确的方法是使用TryGetValue

obj item;
if(!dict.TryGetValue(name, out item))
    return null;
return item;

这只能访问字典一次而不是两次 如果您确实想要返回null,如果该密钥不存在,则可以进一步简化上述代码:

obj item;
dict.TryGetValue(name, out item);
return item;

这很有效,因为如果没有TryGetValue的密钥存在,item会将null设置为name

答案 1 :(得分:6)

字典专门用于执行超快速键查找。它们作为哈希表实现,条目越多,它们相对于其他方法就越快。使用异常引擎只应该在您的方法未能按照您的设计要求执行时执行,因为它是一组大的对象,为您提供了许多处理错误的功能。我创建了一个完整的库类,其中包含一次try catch块所包围的所有内容,并且看到调试输出包含600多个异常中的每一个的单独行,感到震惊!