236

想象一下代码:

public class obj
{
    // elided
}

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

方法一

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

方法二

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

我很好奇这两个函数的性能是否存在差异,因为第一个函数应该比第二个函数慢 - 考虑到它需要检查字典是否包含一个值,而第二个函数确实只需要访问字典一次但哇,它实际上是相反的:

循环 1 000 000 个值(现有 100 000 个,不存在 900 000 个):

第一个函数:306 毫秒

第二个函数:20483毫秒

这是为什么?

编辑:正如您在此问题下方的评论中注意到的那样,如果有 0 个不存在的键,第二个功能的性能实际上比第一个功能略好。但是一旦有至少 1 个或多个不存在的密钥,第二个的性能就会迅速下降。

4

2 回答 2

408

一方面,抛出异常本质上是昂贵的,因为堆栈必须展开等。
另一方面,通过键访问字典中的值很便宜,因为它是一个快速的 O(1) 操作。

顺便说一句:正确的方法是使用TryGetValue

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

这仅访问字典一次而不是两次。
如果你真的想null在 key 不存在的情况下返回,上面的代码可以进一步简化:

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

这有效,因为TryGetValue设置itemnull如果不name存在密钥。

于 2013-04-19T09:48:33.360 回答
6

字典是专门为进行超快速键查找而设计的。它们被实现为哈希表,相对于其他方法,条目越多,它们的速度就越快。仅当您的方法未能完成您设计的工作时才应该使用异常引擎,因为它是一个大型对象集,为您提供了很多处理错误的功能。我曾经构建了一个完整的库类,所有内容都被 try catch 块包围,并且震惊地看到调试输出包含一个单独的行,其中包含 600 多个异常中的每一个!

于 2013-04-23T22:20:37.047 回答