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

想象一下代码:

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,它实际上是相反的:

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

第一个function:306毫秒

第二个function:20483毫秒

这是为什么?

编辑:正如你可以注意到在这个问题下面的评论,第二个函数的性能实际上比第一个好一些,如果有0个不存在的键。 但一旦至less有一个或多个不存在的密钥,第二个密钥的性能会迅速下降。

一方面, 抛出exception本质上是昂贵的 ,因为堆栈必须被解开等等。
另一方面,通过密钥访问字典中的值是便宜的,因为这是一个快速的O(1)操作。

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

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

这只访问一次而不是两次。
如果你真的想只是返回null如果该键不存在,上面的代码可以进一步简化:

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

这是有效的,因为TryGetValue设置itemnull如果没有name键存在。

字典是专门devise用来做超快速键查找的。 它们被实现为哈希表,越多的条目相对于其他方法越快。 使用exception引擎只能在你的方法没有做到你devise的时候完成,因为它是一个很大的对象,给你很多处理错误的function。 我曾经build立过一个完整的库类,其中一切都被try catch块包围了,而且惊慌地发现debugging输出包含了600多个exception中的每一个的单独一行。