web-dev-qa-db-ja.com

辞書にキーが含まれているかどうかをチェックするほうが、含まれていない場合に例外をキャッチするよりも速いのはなぜですか。

コードを想像してみてください。

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;
    }
}

これら2つの関数のパフォーマンスに違いがあるかどうかに興味がありました。最初の関数は2番目の関数より遅くなるべきです - 辞書に値が含まれる場合は2回チェックする必要があるのWOWを除いて、それは実際には反対です。

1000 000個の値をループします(存在する100,000個と存在しない900,000個)。

最初の機能:306ミリ秒

2番目の機能:20483ミリ秒

何故ですか?

編集:あなたがこの質問の下のコメントで気づくことができるように、2番目の機能のパフォーマンスは実際には0の存在しないキーがある場合に最初のものよりわずかに良いです。しかし、存在しないキーが少なくとも1つ以上存在すると、2番目のキーのパフォーマンスは急激に低下します。

234
Petr

一方では、 例外を投げることは本質的に高価です 、スタックを巻き戻す必要があるためなど。
一方、辞書の値にそのキーでアクセスするのは、それが高速なO(1)操作であるため、安価です。

ところで:これを行うための正しい方法は TryGetValue を使用することです。

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

これは、2回ではなく1回だけ辞書にアクセスします。
キーが存在しない場合に本当にnullを返したいだけであれば、上記のコードをさらに単純化することができます。

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

TryGetValueは、itemを持つキーが存在しない場合にnullnameに設定するため、これは機能します。

399
Daniel Hilgarth

辞書は特に超高速のキー検索をするように設計されています。それらはハッシュテーブルとして実装されており、より多くのエントリがあるほど、それらは他の方法と比べて速くなります。例外エンジンの使用は、エラーを処理するための多くの機能を提供する多数のオブジェクトセットであるため、メソッドが設計したとおりに動作しなかった場合にのみ実行されることになっています。私はtry catchブロックで囲まれたものすべてを使ってライブラリクラス全体を1回構築しましたが、600を超える例外のそれぞれについて別々の行が含まれるデバッグ出力を見ることができてうれしいです。

6
Ed Hermanson