보다 효율적인 것은 Dictionary TryGetValue 또는 ContainsKey + Item입니까? Item 속성에서 발생하는 KeyNotFoundException을 잡는 것보다

Dictionary.TryGetValue 메서드 의 MSDN 항목에서 :

이 메서드는 ContainsKey 메서드의 기능과 Item 속성을 결합합니다.

키를 찾을 수 없으면 값 매개 변수는 값 유형 TValue에 적절한 기본값을 가져옵니다. 예를 들어 정수 유형의 경우 0, 부울 유형의 경우 false, 참조 유형의 경우 null입니다.

코드에서 사전에없는 키에 자주 액세스하려고하면 TryGetValue 메서드를 사용하십시오. 이 메소드를 사용하면 Item 속성에서 발생하는 KeyNotFoundException을 잡는 것보다 효율적입니다.

이 방법은 O (1) 연산에 접근합니다.

설명에서 ContainsKey를 호출 한 다음 조회를 수행하는 것보다 더 효율적이거나 더 편리한 지 명확하지 않습니다. 구현은 TryGetValueContainsKey를 호출 한 다음 Item을 호출 합니까 아니면 단일 조회를 수행하는 것보다 실제로 더 효율적입니까?

다시 말해,보다 효율적인 것은 무엇입니까 (즉, 어느 것이 더 적은 조회를 수행 하는가) :

Dictionary<int,int> dict;
//...//
int ival;
if(dict.ContainsKey(ikey))
{
  ival = dict[ikey];
}
else
{
  ival = default(int);
}

또는

Dictionary<int,int> dict;
//...//
int ival;
dict.TryGetValue(ikey, out ival);

참고 : 나는 벤치 마크를 찾고 있지 않습니다!



답변

TryGetValue 더 빠를 것입니다.

ContainsKeyTryGetValue내부적으로 실제 입력 위치를 나타내는와 동일한 검사를 사용합니다 . 이 Item속성은 실제로 TryGetValuefalse와 같은 예외를 throw한다는 점을 제외하고는 코드 기능과 거의 동일합니다 .

ContainsKey그 다음에 Item기본적으로 사용 되는 조회 기능이 복제되며,이 경우 대부분의 계산이 수행됩니다.


답변

빠른 벤치 마크는 TryGetValue약간의 우위를 보여줍니다 .

    static void Main() {
        var d = new Dictionary<string, string> {{"a", "b"}};
        var start = DateTime.Now;
        for (int i = 0; i != 10000000; i++) {
            string x;
            if (!d.TryGetValue("a", out x)) throw new ApplicationException("Oops");
            if (d.TryGetValue("b", out x)) throw new ApplicationException("Oops");
        }
        Console.WriteLine(DateTime.Now-start);
        start = DateTime.Now;
        for (int i = 0; i != 10000000; i++) {
            string x;
            if (d.ContainsKey("a")) {
                x = d["a"];
            } else {
                x = default(string);
            }
            if (d.ContainsKey("b")) {
                x = d["b"];
            } else {
                x = default(string);
            }
        }
   }

이것은 생산

00:00:00.7600000
00:00:01.0610000

제작 ContainsKey + Item40 % 느린 안타 미스의도 조화를 가정에 대한 액세스를.

또한 프로그램을 항상 그리워 (즉, 항상 찾고 "b") 변경하면 두 버전이 똑같이 빨라집니다.

00:00:00.2850000
00:00:00.2720000

그러나 내가 “모든 히트”를 할 때, TryGetValue여전히 확실한 승자입니다.

00:00:00.4930000
00:00:00.8110000

답변

지금까지 답변 중 어느 것도 실제로 질문에 대답하지 않았으므로 다음은 몇 가지 연구 후에 찾은 수용 가능한 답변입니다.

TryGetValue를 디 컴파일하면 다음을 수행하는 것을 볼 수 있습니다.

public bool TryGetValue(TKey key, out TValue value)
{
  int index = this.FindEntry(key);
  if (index >= 0)
  {
    value = this.entries[index].value;
    return true;
  }
  value = default(TValue);
  return false;
}

ContainsKey 메소드는 다음과 같습니다.

public bool ContainsKey(TKey key)
{
  return (this.FindEntry(key) >= 0);
}

따라서 TryGetValue는 ContainsKey와 항목이있는 경우 배열 조회입니다.

출처

TryGetValue는 ContainsKey + Item 조합보다 거의 두 배 빠릅니다.


답변

무슨 상관이야 🙂

아마도 TryGetValue사용 하기 가 어려우므로 아마도 확장 방법으로 캡슐화하십시오.

public static class CollectionUtils
{
    // my original method
    // public static V GetValueOrDefault<K, V>(this Dictionary<K, V> dic, K key)
    // {
    //    V ret;
    //    bool found = dic.TryGetValue(key, out ret);
    //    if (found)
    //    {
    //        return ret;
    //    }
    //    return default(V);
    // }


    // EDIT: one of many possible improved versions
    public static TValue GetValueOrDefault<K, V>(this IDictionary<K, V> dictionary, K key)
    {
        // initialized to default value (such as 0 or null depending upon type of TValue)
        TValue value;

        // attempt to get the value of the key from the dictionary
        dictionary.TryGetValue(key, out value);
        return value;
    }

그런 다음 전화하십시오.

dict.GetValueOrDefault("keyname")

또는

(dict.GetValueOrDefault("keyname") ?? fallbackValue) 

답변

왜 테스트하지 않습니까?

그러나 TryGetValue한 번만 조회하기 때문에 더 빠릅니다. 물론 이것은 보장되지 않습니다. 즉, 구현마다 성능 특성이 다를 수 있습니다.

사전을 구현하는 방법 Find은 항목의 슬롯을 찾는 내부 함수를 만든 다음 나머지를 빌드하는 것입니다.


답변

지금까지의 모든 대답은 훌륭하지만 중요한 요점을 그리워합니다.

API 클래스 (예 : .NET 프레임 워크)에 대한 메소드는 인터페이스 정의 (C # 또는 VB 인터페이스가 아니라 컴퓨터 과학 의미의 인터페이스)의 일부를 형성합니다.

따라서 속도가 공식적인 인터페이스 정의 (이 경우에는 해당되지 않음)의 일부가 아닌 한 이러한 메서드를 호출하는 것이 더 빠른지 묻는 것은 일반적으로 올바르지 않습니다.

전통적으로 이러한 종류의 바로 가기 (검색 및 검색 결합)는 언어, 인프라, OS, 플랫폼 또는 기계 아키텍처에 관계없이 더 효율적입니다. 또한 코드 구조에서 의도를 암시하지 않고 명시 적으로 의도를 표현하기 때문에 더 읽기 쉽습니다.

따라서 어리석은 낡은 핵에서 나온 대답은 ‘예’입니다 (TryGetValue는 Dictionary에서 값을 검색하기 위해 ContainsKey와 Item [Get]의 조합보다 선호됩니다).

이것이 이상하다고 생각되면 다음과 같이 생각하십시오. TryGetValue, ContainsKey 및 Item [Get]의 현재 구현에서 속도 차이가 발생하지 않더라도 향후 구현 (예 : .NET v5) 일 가능성이 있다고 가정 할 수 있습니다. 할 것입니다 (TryGetValue가 빠를 것입니다). 소프트웨어 수명에 대해 생각하십시오.

게다가, 전형적인 최신 인터페이스 정의 기술은 여전히 ​​공식적으로 타이밍 제약을 정의하는 수단을 거의 제공하지 않는다는 점에 주목하는 것이 흥미 롭습니다. .NET v5일까요?


답변

빠른 테스트 프로그램을 만들면 사전에 1 백만 개의 항목이있는 TryGetValue를 사용하여 개선 된 결과가 있습니다.

결과 :

ContainsKey + 1000000 조회수 : 45ms

1000000 조회에 대한 TryGetValue : 26ms

테스트 앱은 다음과 같습니다.

static void Main(string[] args)
{
    const int size = 1000000;

    var dict = new Dictionary<int, string>();

    for (int i = 0; i < size; i++)
    {
        dict.Add(i, i.ToString());
    }

    var sw = new Stopwatch();
    string result;

    sw.Start();

    for (int i = 0; i < size; i++)
    {
        if (dict.ContainsKey(i))
            result = dict[i];
    }

    sw.Stop();
    Console.WriteLine("ContainsKey + Item for {0} hits: {1}ms", size, sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();

    for (int i = 0; i < size; i++)
    {
        dict.TryGetValue(i, out result);
    }

    sw.Stop();
    Console.WriteLine("TryGetValue for {0} hits: {1}ms", size, sw.ElapsedMilliseconds);

}