보다 효율적인 것은 Dictionary TryGetValue 또는 ContainsKey + Item입니까?


251

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

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

답변:


313

TryGetValue 더 빠를 것입니다.

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

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


2
이것은 더 미묘 if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);합니다. 그러나 인덱서 속성 TryGetValue의 get set이 사용 되기 때문에 여전히 더 효율적 이라고 생각 합니까?
Tim Schmelter

4
실제로는 .net 소스도 참조 할 수 있습니다. trygetvalue는 bool과 값을 반환하고 key는 true / false 만 반환하며 this []는 값을 반환하거나 예외를 throw합니다.
John Gardner

1
@ JohnGardner 예, 이것이 내가 말한 것입니다. 그러나 ContainsKey를 수행 한 다음 Item을 얻으면 1x 대신 2x 작업을 수행합니다.
리드 콥시

3
나는 완전히 동의합니다 :) 나는 실제 소스를 사용할 수 있다고 지적했습니다. 다른 답변 / 등은 실제 소스에 대한 링크가 없었습니다 : D
John Gardner

1
다중 스레드 환경에서 IDictionary를 통해 액세스하는 경우 주제를 약간 벗어난 경우 ContainsKey를 호출 할 때부터 상태가 변경 될 수 있으므로 항상 TryGetValue를 사용합니다 (TryGetValue가 내부적으로 올바르게 잠길 것이라는 보장은 없지만 더 안전합니다)
Chris Berry

91

빠른 벤치 마크는 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

11
물론 실제 사용 패턴에 따라 다릅니다. 조회에 거의 실패하지 않으면 TryGetValue훨씬 앞서 있어야합니다. 또한 nitpick DateTime은 성능 측정을 캡처하는 가장 좋은 방법은 아닙니다.
Ed S.

4
@EdS. 당신 말이 맞아요 TryGetValue. "모든 적중"및 "모든 미스"시나리오를 포함하도록 답변을 편집했습니다.
dasblinkenlight

2
@Luciano 사용 방법 설명 Any-다음과 같습니다 Any(i=>i.Key==key).. 어떤 경우에, 그것은 사전에 대한 잘못된 선형 검색입니다.
weston December

13
DateTime.Now몇 ms까지만 정확합니다. 대신 Stopwatch클래스를 사용하십시오 System.Diagnostics(더 높은 정확도를 제공하기 위해 표지 아래의 QueryPerformanceCounter를 사용함). 사용하기도 쉽습니다.
Alastair Maw

5
Alastair 및 Ed의 의견 외에도-DateTime.Now는 사용자가 컴퓨터 시간을 업데이트하거나 시간대가 교차되거나 시간대가 변경 될 때 발생하는 시간 업데이트와 같은 시간 업데이트를 받으면 뒤로 이동할 수 있습니다. 예). GPS 또는 휴대 전화 네트워크와 같은 일부 라디오 서비스에서 제공하는 시간과 시스템 시계가 동기화 된 시스템에서 작업 해보십시오. DateTime.Now는 모든 곳에서 진행되며 DateTime.UtcNow는 이러한 원인 중 하나만 수정합니다. StopWatch를 사용하십시오.
antiduh

51

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

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 조합보다 거의 두 배 빠릅니다.


20

무슨 상관이야 :-)

아마도 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) 

1
@ Hüseyin 나는 이것을 게시 할만 큼 어리석은 방법에 대해 매우 혼란스러워 this했지만 내 코드베이스에 두 번 복제 된 메소드가 있음이 밝혀졌습니다 this. 수정 해 주셔서 감사합니다!
Simon_Weaver

2
TryGetValue키가 없으면 기본값을 출력 값 매개 변수에 지정하므로이를 단순화 할 수 있습니다.
Raphael Smit

2
단순화 된 버전 : public static TValue GetValueOrDefault <TKey, TValue> (이 Dictionary <TKey, TValue> dict, TKey 키) {TValue ret; dict.TryGetValue (키, ret); 리트 윗을 반환; }
Joshua

2
C # 7 이것은 재미는 정말 :if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
흔들 Weitzhandler

1
아이러니하게도, 실제 소스 코드는 이미 GetValueOrDefault () 루틴을 가지고 있지만 ... 숨겨진 referencesource.microsoft.com/#mscorlib/system/collections/...
Deven T. 코자

10

왜 테스트하지 않습니까?

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

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


구현 세부 사항이 액션 X를 한 번 수행하는 것이 액션 X를 두 번 수행하는 것보다 빠르거나 같다는 보장을 변경할 수 있다고 생각하지 않습니다. 가장 좋은 경우는 동일하지만 더 나쁜 경우 2X 버전은 두 배의 시간이 걸립니다.
Dan Bechard

9

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

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

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

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

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

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

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


2
시맨틱에 대한 귀하의 주장에 100 % 동의하지만, 성능 테스트는 여전히 가치가 있습니다. 테스트를 수행하지 않는 한 사용하는 API에 의미가 맞지 않는 구현이 느리게 구현되는시기를 절대 알 수 없습니다.
Dan Bechard

5

빠른 테스트 프로그램을 만들면 사전에 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);

}

5

내 컴퓨터에서 RAM이 많은 상태에서 RELEASE 모드 (DEBUG 아님)로 실행될 때 /에 해당하는 모든 항목이 있으면 / ContainsKey와 같습니다 .TryGetValuetry-catchDictionary<>

ContainsKey몇 가지 사전 항목을 찾을 수없는 경우 (아래 예에서는 일부 항목이 누락 된 MAXVAL것보다 큰 값 ENTRIES으로 설정 됨) 훨씬 더 우수 합니다.

결과 :

Finished evaluation .... Time distribution:
Size: 000010: TryGetValue: 53,24%, ContainsKey: 1,74%, try-catch: 45,01% - Total: 2.006,00
Size: 000020: TryGetValue: 37,66%, ContainsKey: 0,53%, try-catch: 61,81% - Total: 2.443,00
Size: 000040: TryGetValue: 22,02%, ContainsKey: 0,73%, try-catch: 77,25% - Total: 7.147,00
Size: 000080: TryGetValue: 31,46%, ContainsKey: 0,42%, try-catch: 68,12% - Total: 17.793,00
Size: 000160: TryGetValue: 33,66%, ContainsKey: 0,37%, try-catch: 65,97% - Total: 36.840,00
Size: 000320: TryGetValue: 34,53%, ContainsKey: 0,39%, try-catch: 65,09% - Total: 71.059,00
Size: 000640: TryGetValue: 32,91%, ContainsKey: 0,32%, try-catch: 66,77% - Total: 141.789,00
Size: 001280: TryGetValue: 39,02%, ContainsKey: 0,35%, try-catch: 60,64% - Total: 244.657,00
Size: 002560: TryGetValue: 35,48%, ContainsKey: 0,19%, try-catch: 64,33% - Total: 420.121,00
Size: 005120: TryGetValue: 43,41%, ContainsKey: 0,24%, try-catch: 56,34% - Total: 625.969,00
Size: 010240: TryGetValue: 29,64%, ContainsKey: 0,61%, try-catch: 69,75% - Total: 1.197.242,00
Size: 020480: TryGetValue: 35,14%, ContainsKey: 0,53%, try-catch: 64,33% - Total: 2.405.821,00
Size: 040960: TryGetValue: 37,28%, ContainsKey: 0,24%, try-catch: 62,48% - Total: 4.200.839,00
Size: 081920: TryGetValue: 29,68%, ContainsKey: 0,54%, try-catch: 69,77% - Total: 8.980.230,00

내 코드는 다음과 같습니다.

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                const int ENTRIES = 10000, MAXVAL = 15000, TRIALS = 100000, MULTIPLIER = 2;
                Dictionary<int, int> values = new Dictionary<int, int>();
                Random r = new Random();
                int[] lookups = new int[TRIALS];
                int val;
                List<Tuple<long, long, long>> durations = new List<Tuple<long, long, long>>(8);

                for (int i = 0;i < ENTRIES;++i) try
                    {
                        values.Add(r.Next(MAXVAL), r.Next());
                    }
                    catch { --i; }

                for (int i = 0;i < TRIALS;++i) lookups[i] = r.Next(MAXVAL);

                Stopwatch sw = new Stopwatch();
                ConsoleColor bu = Console.ForegroundColor;

                for (int size = 10;size <= TRIALS;size *= MULTIPLIER)
                {
                    long a, b, c;

                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Loop size: {0}", size);
                    Console.ForegroundColor = bu;

                    // ---------------------------------------------------------------------
                    sw.Start();
                    for (int i = 0;i < size;++i) values.TryGetValue(lookups[i], out val);
                    sw.Stop();
                    Console.WriteLine("TryGetValue: {0}", a = sw.ElapsedTicks);

                    // ---------------------------------------------------------------------
                    sw.Restart();
                    for (int i = 0;i < size;++i) val = values.ContainsKey(lookups[i]) ? values[lookups[i]] : default(int);
                    sw.Stop();
                    Console.WriteLine("ContainsKey: {0}", b = sw.ElapsedTicks);

                    // ---------------------------------------------------------------------
                    sw.Restart();
                    for (int i = 0;i < size;++i)
                        try { val = values[lookups[i]]; }
                        catch { }
                    sw.Stop();
                    Console.WriteLine("try-catch: {0}", c = sw.ElapsedTicks);

                    // ---------------------------------------------------------------------
                    Console.WriteLine();

                    durations.Add(new Tuple<long, long, long>(a, b, c));
                }

                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Finished evaluation .... Time distribution:");
                Console.ForegroundColor = bu;

                val = 10;
                foreach (Tuple<long, long, long> d in durations)
                {
                    long sum = d.Item1 + d.Item2 + d.Item3;

                    Console.WriteLine("Size: {0:D6}:", val);
                    Console.WriteLine("TryGetValue: {0:P2}, ContainsKey: {1:P2}, try-catch: {2:P2} - Total: {3:N}", (decimal)d.Item1 / sum, (decimal)d.Item2 / sum, (decimal)d.Item3 / sum, sum);
                    val *= MULTIPLIER;
                }

                Console.WriteLine();
            }
        }
    }

나는 뭔가 비린내가 진행되고있는 것 같은 느낌이 든다. 검색 된 값을 절대 사용하지 않기 때문에 옵티마이 저가 ContainsKey () 검사를 제거하거나 단순화하는지 궁금합니다.
Dan Bechard

그냥 할 수 없습니다. ContainsKey ()가 컴파일 된 DLL에 있습니다. 옵티마이 저는 ContainsKey ()가 실제로 무엇을하는지 전혀 모른다. 부작용을 일으킬 수 있으므로 호출해야하며 요약 할 수 없습니다.
AxD

뭔가 가짜입니다. 사실 .NET 코드를 검사하면 ContainsKey, TryGetValue 및 this []가 모두 동일한 내부 코드를 호출하므로 TryGetValue가 항목이있을 때 ContainsKey + this []보다 빠릅니다.
Jim Balter

3

실제 설정에서 정확한 결과를 제공하는 마이크로 벤치 마크를 설계하는 것 외에도 .NET Framework의 참조 소스를 검사 할 수 있습니다.

그들 모두 FindEntry(TKey)는 대부분의 작업을 수행 하는 메소드를 호출 하고 결과를 기억하지 않으므로 호출 TryGetValueContainsKey+ 보다 거의 두 배 빠릅니다Item .


불편한 인터페이스 는 확장 방법을 사용하여TryGetValue 조정할 수 있습니다 .

using System.Collections.Generic;

namespace Project.Common.Extensions
{
    public static class DictionaryExtensions
    {
        public static TValue GetValueOrDefault<TKey, TValue>(
            this IDictionary<TKey, TValue> dictionary,
            TKey key,
            TValue defaultValue = default(TValue))
        {
            if (dictionary.TryGetValue(key, out TValue value))
            {
                return value;
            }
            return defaultValue;
        }
    }
}

C # 7.1부터는 default(TValue)plain로 바꿀 수 있습니다 default. 유형이 유추됩니다.

용법:

var dict = new Dictionary<string, string>();
string val = dict.GetValueOrDefault("theKey", "value used if theKey is not found in dict");

null명시 적 기본값을 지정하지 않으면 조회가 실패한 참조 유형에 대해 리턴 합니다.

var dictObj = new Dictionary<string, object>();
object valObj = dictObj.GetValueOrDefault("nonexistent");
Debug.Assert(valObj == null);

val dictInt = new Dictionary<string, int>();
int valInt = dictInt.GetValueOrDefault("nonexistent");
Debug.Assert(valInt == 0);

확장 메서드 사용자는 존재하지 않는 키와 존재하는 키의 차이점을 알 수 없지만 해당 값은 default (T)입니다.
Lucas

최신 컴퓨터에서 서브 루틴을 빠르게 두 번 연속 호출하면 한 번 호출하는 것보다 두 배가 걸리지 않습니다. 이는 CPU 및 캐싱 아키텍처가 첫 번째 호출과 관련된 많은 명령과 데이터를 캐시 할 가능성이 높으므로 두 번째 호출이 더 빨리 실행되기 때문입니다. 반면에 두 번 전화하는 것은 한 번 전화하는 것보다 시간이 조금 더 걸리기 때문에 가능한 경우 두 번째 전화를 제거하는 것이 여전히 유리합니다.
토론자

2

사전에서 값을 가져 오려는 경우 TryGetValue (key, out value)가 가장 좋은 옵션이지만 이전 키를 덮어 쓰지 않고 키가 있는지 확인하고 새 삽입을하려면 해당 범위 내에서만 ContainsKey (key)가 가장 좋은 옵션이며 벤치 마크에서이를 확인할 수 있습니다.

using System;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;

namespace benchmark
{
class Program
{
    public static Random m_Rand = new Random();
    public static Dictionary<int, int> testdict = new Dictionary<int, int>();
    public static Hashtable testhash = new Hashtable();

    public static void Main(string[] args)
    {
        Console.WriteLine("Adding elements into hashtable...");
        Stopwatch watch = Stopwatch.StartNew();
        for(int i=0; i<1000000; i++)
            testhash[i]=m_Rand.Next();
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- pause....", watch.Elapsed.TotalSeconds);
        Thread.Sleep(4000);
        Console.WriteLine("Adding elements into dictionary...");
        watch = Stopwatch.StartNew();
        for(int i=0; i<1000000; i++)
            testdict[i]=m_Rand.Next();
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- pause....", watch.Elapsed.TotalSeconds);
        Thread.Sleep(4000);

        Console.WriteLine("Finding the first free number for insertion");
        Console.WriteLine("First method: ContainsKey");
        watch = Stopwatch.StartNew();
        int intero=0;
        while (testdict.ContainsKey(intero))
        {
            intero++;
        }
        testdict.Add(intero, m_Rand.Next());
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- added value {1} in dictionary -- pause....", watch.Elapsed.TotalSeconds, intero);
        Thread.Sleep(4000);
        Console.WriteLine("Second method: TryGetValue");
        watch = Stopwatch.StartNew();
        intero=0;
        int result=0;
        while(testdict.TryGetValue(intero, out result))
        {
            intero++;
        }
        testdict.Add(intero, m_Rand.Next());
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- added value {1} in dictionary -- pause....", watch.Elapsed.TotalSeconds, intero);
        Thread.Sleep(4000);
        Console.WriteLine("Test hashtable");
        watch = Stopwatch.StartNew();
        intero=0;
        while(testhash.Contains(intero))
        {
            intero++;
        }
        testhash.Add(intero, m_Rand.Next());
        watch.Stop();
        Console.WriteLine("Done in {0:F4} -- added value {1} into hashtable -- pause....", watch.Elapsed.TotalSeconds, intero);
        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }
}
}

이것은 진정한 예입니다. 나는 각 "항목"에 대해 생성 된 서비스를 가지고 있으며, 새로운 번호를 생성 할 때마다이 번호는 점진적 번호를 연결합니다. 항목을 삭제하면 무료 번호가됩니다. 무료, 물론 이것은 현재 숫자를 캐시하는 정적 var가 있기 때문에 최적화되지 않았지만 모든 숫자를 끝내는 경우 0에서 UInt32로 다시 시작할 수 있습니다.

테스트 실행 :
해시 테이블에 요소 추가 ...
0,5908에서 완료-일시 정지 ....
사전에 요소 추가 ...
0,2679에서 완료-일시 정지 ...
삽입을위한 첫 번째 자유 번호 찾기
첫 번째 방법 : ContainsKey
완료 0,0561-사전에 값 1000000 추가-일시 정지 ....
두 번째 방법 : TryGetValue
완료 0,0643-사전에 값 1000001 추가-일시 정지 ... 0에서
해시 테이블
완료 테스트 , 3015-해시 테이블에 1000000 값을 추가-일시 중지 ..
계속하려면 아무 키나 누르 십시오. .

ContainsKeys가 이점을 가질 수 있는지 묻는 사용자 중 일부가 Contains 키를 사용하여 TryGetValue를 반전하려고 시도한 경우에도 결과는 동일합니다.

따라서 마지막으로 고려해야 할 것은 프로그램의 작동 방식에 달려 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.