क्या अधिक कुशल है: शब्दकोश TryGetValue या ContainsKey + आइटम?


251

MSDN के प्रवेश पर से DictionaryTryGetValue विधि :

यह विधि ContainsKey विधि और आइटम गुण की कार्यक्षमता को जोड़ती है।

यदि कुंजी नहीं मिली है, तो मान पैरामीटर को मान प्रकार TValue के लिए उपयुक्त डिफ़ॉल्ट मान मिलता है; उदाहरण के लिए, पूर्णांक प्रकारों के लिए 0 (शून्य), बुलियन प्रकारों के लिए गलत और संदर्भ प्रकारों के लिए शून्य।

TryGetValue विधि का उपयोग करें यदि आपका कोड अक्सर उन कुंजियों तक पहुंचने का प्रयास करता है जो शब्दकोश में नहीं हैं। इस गुण का उपयोग करना आइटम गुण द्वारा फेंके गए KeyNotFoundException को पकड़ने से अधिक कुशल है।

यह विधि एक O (1) ऑपरेशन के पास जाती है।

विवरण से, यह स्पष्ट नहीं है कि क्या यह ContainsKey को कॉल करने की तुलना में अधिक कुशल या सिर्फ अधिक सुविधाजनक है और फिर लुकअप कर रहा है। क्या TryGetValueकेवल ContainsKey को कॉल करना और फिर आइटम को लागू करना या वास्तव में एकल लुकअप करने से अधिक कुशल है?

दूसरे शब्दों में, क्या अधिक कुशल है (यानी जो कम लुकअप करता है):

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 तेज हो जाएगा।

ContainsKeyके रूप में एक ही चेक का उपयोग करता है TryGetValue, जो आंतरिक रूप से वास्तविक प्रवेश स्थान को संदर्भित करता है। Itemसंपत्ति वास्तव में के रूप में लगभग समान कोड कार्यक्षमता हैTryGetValue सिवाय इसके कि यह झूठी लौटने के बजाय एक अपवाद फेंक देंगे।

का उपयोग कर ContainsKeyके बाद Itemमूल रूप से देखने कार्यक्षमता, जो इस मामले में गणना के थोक है डुप्लिकेट।


2
यह अधिक सूक्ष्म है if(dict.ContainsKey(ikey)) dict[ikey]++; else dict.Add(ikey, 0);:। लेकिन मुझे लगता है कि इंडेक्सर संपत्ति के उपयोग और सेट के TryGetValueबाद से यह अभी भी अधिक कुशल है, क्या यह नहीं है?
टिम शाल्टर

4
आप वास्तव में अब इसके लिए .net स्रोत देख सकते हैं भी: referencesource.microsoft.com/#mscorlib/system/collections/... आप देख सकते हैं कि TryGetValue, ContainsKey, और इस [] एक ही FindEntry विधि कॉल और कर के सभी 3 काम की एक ही राशि, केवल इस बात में अंतर करने पर कि वे कैसे प्रश्न का उत्तर देते हैं: trygetvalue बूल और मान लौटाता है, जिसमें कुंजी केवल सही / गलत होती है, और यह [] मान लौटाता है या एक अपवाद फेंकता है।
जॉन गार्डनर

1
@ जॉनहार्डनर हां, जो मैंने कहा है - लेकिन अगर आप कॉन्टेंसकेय करते हैं तो आइटम प्राप्त करें, आप 1x के बजाय उस काम को 2x कर रहे हैं।
रीड कोपसे

3
मैं पूरी तरह से सहमत हूं :) मैं सिर्फ इशारा कर रहा था कि वास्तविक स्रोत अब उपलब्ध है। अन्य उत्तर / आदि में से किसी का भी वास्तविक स्रोत से लिंक नहीं था: D
जॉन गार्डनर

1
थोड़ा दूर विषय, यदि आप एक multithreaded वातावरण में एक IDEDIA के माध्यम से पहुँच रहे हैं, तो मैं हमेशा TryGetValue का उपयोग करूँगा क्योंकि राज्य उस समय से बदल सकता है जब आप ContainsKey कहते हैं (कोई गारंटी नहीं है कि TryGetValue आंतरिक रूप से या तो सही ढंग से लॉक है, लेकिन यह शायद सुरक्षित है)
क्रिस बेरी

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 + Itemके लिए 40% धीमी हिट और चूक की एक और भी मिश्रण संभालने के बारे में उपयोग।

इसके अलावा, जब मैं प्रोग्राम को हमेशा याद रखने के लिए बदलता हूं (यानी हमेशा देखता रहता हूं "b") दो संस्करण समान रूप से तेज हो जाते हैं:

00:00:00.2850000
00:00:00.2720000

जब मैं इसे "सभी हिट" बनाता हूं, हालांकि, TryGetValueएक स्पष्ट विजेता रहता है:

00:00:00.4930000
00:00:00.8110000

11
बेशक, यह वास्तविक उपयोग पैटर्न पर निर्भर करता है। यदि आप लगभग कभी भी एक लुकअप को विफल नहीं करते हैं, तो TryGetValueबहुत आगे होना चाहिए। इसके अलावा ... DateTimeप्रदर्शन माप को पकड़ने का सबसे अच्छा तरीका नहीं है।
एड एस।

4
@EdS। आप सही कह रहे हैं, TryGetValueआगे भी बढ़त में है। मैंने "सभी हिट" और "सभी मिस" परिदृश्यों को शामिल करने के लिए उत्तर को संपादित किया।
dasblinkenlight

2
@ लुसियानो ने बताया कि आपने कैसे इस्तेमाल किया Any- इस तरह Any(i=>i.Key==key):। किस मामले में, हां, यह शब्दकोश की एक बुरी रैखिक खोज है।
पश्चिम दिशा में

13
DateTime.Nowकेवल कुछ एमएस के लिए सटीक होगा। इसके बजाय Stopwatchकक्षा का उपयोग करें System.Diagnostics(जो बहुत अधिक सटीकता प्रदान करने के लिए कवर के तहत QueryPerformanceCounter का उपयोग करता है)। इसका उपयोग करना आसान है, भी।
एलेस्टेयर माव

5
एलेस्टेयर और एड की टिप्पणियों के अलावा - DateTime.Now पीछे जा सकते हैं, यदि आपको कोई समय अद्यतन मिलता है, जैसे कि जो तब होता है जब उपयोगकर्ता अपना कंप्यूटर समय अपडेट करता है, एक समय क्षेत्र पार हो जाता है, या समय क्षेत्र बदल जाता है (DST, के लिए) उदाहरण)। एक ऐसी प्रणाली पर काम करने की कोशिश करें जिसमें सिस्टम घड़ी हो, जो समय-समय पर कुछ रेडियो सेवा जैसे जीपीएस या मोबाइल फोन नेटवर्क द्वारा प्रदान की जाती है। DateTime.Now सभी जगह जाएगा, और DateTime.UtcNow केवल उन कारणों में से एक को ठीक करता है। बस स्टॉपवॉच का उपयोग करें।
प्रतिपादु

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 + आइटम संयोजन के रूप में तेजी से होगा।


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
@ हुसेन मैं बहुत उलझन में था कि मैं इसे पोस्ट करने के लिए काफी बेवकूफ था, thisलेकिन यह पता चला कि मेरे पास मेरे कोड बेस में दो बार दोहराए गए तरीके हैं - एक बार और एक बिना इस thisकारण कि मैंने इसे कभी नहीं पकड़ा! फिक्सिंग के लिए धन्यवाद!
साइमन_वेअर जूल

2
TryGetValueकुंजी मान मौजूद नहीं होने पर आउट मान पैरामीटर को एक डिफ़ॉल्ट मान प्रदान करता है, इसलिए इसे सरल बनाया जा सकता है।
राफेल स्मिट

2
सरलीकृत संस्करण: सार्वजनिक स्थिर TValue GetValueOrDefault <TKey, TValue> (यह शब्दकोश <TKey, TValue> तानाशाह, TKey कुंजी) {TValue ret; ताना.ट्रीगेटवैल्यू (की, आउट रिट); वापसी; }
जोशुआ

2
C # 7 में यह वास्तव में मजेदार है:if(!dic.TryGetValue(key, out value item)) item = dic[key] = new Item();
शिम्मी वेइटहैंडलर

1
विडंबना यह है कि वास्तविक स्रोत कोड पहले से ही एक GetValueOrDefault () दिनचर्या है, लेकिन यह छिपा हुआ है ... referencesource.microsoft.com/#mscorlib/system/collections/...
देवेन टी Corzine

10

आप इसका परीक्षण क्यों नहीं करते?

लेकिन मुझे पूरा यकीन है TryGetValue यह तेज़ है, क्योंकि यह केवल एक ही खोज करता है। बेशक इसकी गारंटी नहीं है, यानी अलग-अलग कार्यान्वयन में अलग-अलग प्रदर्शन विशेषताएं हो सकती हैं।

जिस तरह से मैं एक शब्दकोश को लागू करता हूं वह एक आंतरिक Findफ़ंक्शन बनाकर होता है जो किसी आइटम के लिए स्लॉट ढूंढता है, और उसके बाद बाकी का निर्माण करता है।


मुझे नहीं लगता कि कार्यान्वयन विवरण संभवतः गारंटी को बदल सकता है कि एक्शन एक्स को एक बार करने की तुलना में दो बार एक्शन एक्स या उससे तेज है। सबसे अच्छा मामला वे समान हैं, बदतर मामला 2X संस्करण के रूप में दो बार लंबे समय तक ले जाता है।
दान बिक्र्ड

9

अब तक के सभी उत्तर, हालांकि अच्छे हैं, एक महत्वपूर्ण बिंदु याद करते हैं।

API की कक्षाओं में विधियाँ (जैसे .NET ढाँचा) इंटरफ़ेस परिभाषा का हिस्सा बनती हैं (C # या VB इंटरफ़ेस नहीं, बल्कि कंप्यूटर विज्ञान अर्थ में एक इंटरफ़ेस)।

जैसे, यह पूछना आमतौर पर गलत है कि क्या इस तरह की कॉलिंग तेज है, जब तक कि गति औपचारिक इंटरफ़ेस परिभाषा का एक हिस्सा नहीं है (जो कि इस मामले में नहीं है)।

परंपरागत रूप से इस तरह के शॉर्टकट भाषा (इन्फ्रास्ट्रक्चर, ओएस, प्लेटफ़ॉर्म, या मशीन आर्किटेक्चर) की परवाह किए बिना अधिक कुशल (खोज और पुनः प्राप्त करना) है। यह अधिक पठनीय भी है, क्योंकि यह आपके इरादे को स्पष्ट रूप से व्यक्त करता है, बजाय इसे लागू करने के (आपके कोड की संरचना से)।

तो उत्तर (एक घिनौनी पुरानी हैक से) निश्चित रूप से 'हां' है (ट्राइगेटवैल्यू कॉन्टेन्सीके और आइटम के संयोजन के लिए बेहतर है [प्राप्त करें] एक शब्दकोश से एक मान प्राप्त करने के लिए)।

यदि आपको लगता है कि यह अजीब लगता है, तो इसे इस तरह से सोचें: भले ही TryGetValue, ContainsKey, और आइटम के वर्तमान कार्यान्वयन [प्राप्त करें] किसी भी गति अंतर को उत्पन्न नहीं करते हैं, आप यह मान सकते हैं कि भविष्य में कार्यान्वयन (उदाहरण .NET 5/5) करेंगे (TryGetValue तेज हो जाएगा)। अपने सॉफ़्टवेयर के जीवनकाल के बारे में सोचें।

एक तरफ के रूप में, यह ध्यान रखना दिलचस्प है कि ठेठ आधुनिक इंटरफ़ेस परिभाषा तकनीक अभी भी शायद ही कभी औपचारिक रूप से समय की कमी को परिभाषित करने का कोई साधन प्रदान करती है। शायद .NET v5?


2
जबकि मैं 100% शब्दार्थ के बारे में आपके तर्क से सहमत हूँ, फिर भी यह प्रदर्शन परीक्षण करने लायक है। आपको कभी पता नहीं चलता कि आप जिस एपीआई का उपयोग कर रहे हैं, उसका उप-कार्यान्वयन कार्यान्वयन ऐसा है कि शब्दशः सही बात धीमी होती है, जब तक कि आप परीक्षण नहीं करते।
डैन बेहार्ड

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बराबर होता है TryGetValue/ try-catchयदि सभी में प्रवेश होता हैDictionary<> पाई जाती हैं।

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 () चेक को हटा या सरल कर सकता है, तो इस तथ्य के कारण कि आप कभी भी पुनर्प्राप्त मूल्य का उपयोग नहीं करते हैं।
दान बिक्र्ड

यह सिर्फ नहीं कर सकता। ContainsKey () एक संकलित DLL में है। आशावादी को कुछ भी पता नहीं है कि वास्तव में ContainsKey () क्या करता है। यह दुष्प्रभाव हो सकता है, इसलिए इसे बुलाया जाना चाहिए और इसे समाप्त नहीं किया जा सकता है।
AxD

यहां कुछ फर्जी है। तथ्य यह है कि .NET कोड की जांच करने से पता चलता है कि ContainsKey, TryGetValue, और यह [] सभी एक ही आंतरिक कोड कहते हैं, इसलिए TryGetValue ContainsKey + इस []] की तुलना में तेज़ है जब प्रविष्टि मौजूद है।
जिम बैटर

3

माइक्रोएन्चमार्क को डिज़ाइन करने के अलावा, जो व्यावहारिक सेटिंग में सटीक परिणाम देगा, आप .NET फ्रेमवर्क के संदर्भ स्रोत का निरीक्षण कर सकते हैं।

वे सभी उस FindEntry(TKey)विधि को कहते हैं जो अधिकांश कार्य करता है और इसके परिणाम को याद नहीं करता है, इसलिए कॉलिंग + के TryGetValueरूप में लगभग दो बार तेज हैContainsKeyItem


एक्सटेंशन पद्धति का उपयोग करके असुविधाजनक इंटरफ़ेस को अनुकूलित किया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)सादे से बदल सकते हैं 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);

ध्यान दें कि एक्सटेंशन विधि के उपयोगकर्ता गैर-मौजूद कुंजी और मौजूद कुंजी के बीच अंतर नहीं बता सकते हैं, लेकिन इसका मान डिफ़ॉल्ट (T) है।
लुकास

एक आधुनिक कंप्यूटर पर, यदि आप त्वरित उत्तराधिकार में दो बार एक सबरूटीन कहते हैं, तो इसे एक बार कॉल करने में दो बार लेने की संभावना नहीं है। ऐसा इसलिए है क्योंकि सीपीयू और कैशिंग आर्किटेक्चर के पहले कॉल से जुड़े निर्देशों और डेटा को कैश करने की बहुत संभावना है, इसलिए दूसरी कॉल को तेजी से निष्पादित किया जाएगा। दूसरी ओर, एक बार कॉल करने की तुलना में दो बार कॉल करना लगभग निश्चित है, इसलिए संभव के रूप में दूसरा कॉल समाप्त करने का एक फायदा है।
डेब्यू

2

यदि आप शब्दकोश से मान निकालने की कोशिश कर रहे हैं, तो TryGetValue (कुंजी, मूल्य बाहर) सबसे अच्छा विकल्प है, लेकिन यदि आप कुंजी की उपस्थिति के लिए जाँच कर रहे हैं, तो एक नई प्रविष्टि के लिए, पुरानी कुंजियों को अधिलेखित किए बिना, और केवल उस दायरे के साथ, ContainsKey (कुंजी) सबसे अच्छा विकल्प है, बेंचमार्क इस बात की पुष्टि कर सकता है:

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

यह एक सच्चा उदाहरण है, मेरी एक सेवा है जो प्रत्येक "आइटम" के लिए बनाई गई है, यह एक प्रगतिशील संख्या को जोड़ती है, यह संख्या, हर बार जब आप एक नया आइटम बनाते हैं, तो इसे मुफ्त में पाया जाना चाहिए, यदि आप एक आइटम हटाते हैं, तो मुफ्त नंबर बन जाता है। नि: शुल्क, निश्चित रूप से यह अनुकूलित नहीं है, क्योंकि मेरे पास एक स्थिर संस्करण है जो वर्तमान संख्या को कैश करता है, लेकिन यदि आप सभी संख्याओं को समाप्त कर देते हैं, तो आप 0 से UInt32.MaxValue पर फिर से शुरू कर सकते हैं

परीक्षण निष्पादित: हैशटेबल में
तत्वों को जोड़ना ...
0,5908 में किया गया - रोकें ...
शब्दकोश में तत्वों को
जोड़ते हुए ... 0,2679 में पूरा किया - रोकें ...
सम्मिलन के लिए पहला नि: शुल्क नंबर ढूँढना
पहली विधि :
0,0561 में ContainsKey Done - डिक्शनरी में 1000000 जोड़ा गया मान - पॉज़ ....
दूसरा तरीका: 0,0643
में TryGetValue Done - डिक्शनरी में 1000001 जोड़ा गया - पॉज़ .... 0 में
टेस्ट हैशटेबल हो
गया। 3015 - जोड़ा गया मूल्य 1000000 हैशटेबल - पॉज़ में ....
जारी रखने के लिए कोई भी कुंजी दबाएँ। ।

यदि आप में से कुछ पूछ रहे हैं कि क्या ContainsKeys को फायदा हो सकता है, तो मैंने भी प्रयास कुंजी के साथ TryGetValue inverting की कोशिश की है, परिणाम समान है।

इसलिए, मेरे लिए, अंतिम विचार के साथ, यह सब कार्यक्रम के व्यवहार के तरीके पर निर्भर करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.