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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16101795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:00:46  来源:igfitidea点击:

Why is it faster to check if dictionary contains the key, rather than catch the exception in case it doesn't?

c#performancedictionary

提问by Petr

Imagine the code:

想象一下代码:

public class obj
{
    // elided
}

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

Method 1

方法一

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

Method 2

方法二

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

I was curious if there is a difference in performance of these 2 functions, because the first one SHOULD be SLOWER than second one - given that it needs to check twice if the dictionary contains a value, while second function does need to access the dictionary only once but WOW, it's actually opposite:

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

Loop for 1 000 000 values (with 100 000 existing and 900 000 non existing):

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

first function: 306 milliseconds

second function: 20483 milliseconds

第一个函数:306 毫秒

第二个函数:20483 毫秒

Why is that?

这是为什么?

EDIT: As you can notice in comments below this question, the performance of second function is actually slightly better than first one in case there are 0 non existing keys. But once there is at least 1 or more non existing keys, the performance of second one decrease rapidly.

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

采纳答案by Daniel Hilgarth

On the one hand, throwing exceptions is inherently expensive, because the stack has to be unwound etc.
On the other hand, accessing a value in a dictionary by its key is cheap, because it's a fast, O(1) operation.

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

BTW: The correct way to do this is to use TryGetValue

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

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

This accesses the dictionary only once instead of twice.
If you really want to just return nullif the key doesn't exist, the above code can be simplified further:

这只会访问字典一次而不是两次。
如果您真的只想null在键不存在时返回,则可以进一步简化上述代码:

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

This works, because TryGetValuesets itemto nullif no key with nameexists.

这是有效的,因为如果不存在键,则TryGetValue设置item为。nullname

回答by Ed Hermanson

Dictionaries are specifically designed to do super fast key lookups. They are implemented as hashtables and the more entries the faster they are relative to other methods. Using the exception engine is only supposed to be done when your method has failed to do what you designed it to do because it is a large set of object that give you a lot of functionality for handling errors. I built an entire library class once with everything surrounded by try catch blocks once and was appalled to see the debug output which contained a seperate line for every single one of over 600 exceptions!

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