2013-02-20 105 views
12

में जोड़ने के दौरान सभी वस्तुओं के लिए बराबर() को क्यों नहीं कहा जा रहा है मेरे पास एक प्रकार है जिसे मैं IDictionary में कुंजी के रूप में उपयोग कर रहा हूं। प्रकार के रूप में निम्नलिखितसंग्रह

public class Employee 
{ 
    public string Name { get; set; } 
    public int ID { get; set; } 

    public override bool Equals(object obj) 
    { 
     Employee emp = obj as Employee; 
     if (emp != null) 
      return emp.Name.Equals(this.Name); 
     return false; 
    } 

    public override int GetHashCode() 
    { 
     return this.Name.GetHashCode(); 
    } 
} 

अब मैं

IDictionary<Employee, int> empCollection = new Dictionary<Employee, int>(); 
     Employee emp1 = new Employee() { Name = "abhi", ID = 1 }; 
     Employee emp2 = new Employee() { Name = "vikram", ID = 2 }; 
     Employee emp3 = new Employee() { Name = "vikram", ID = 3 }; 

     empCollection.Add(emp1, 1); 
     empCollection.Add(emp2, 2); 
     empCollection.Add(emp3, 3); 

निम्नलिखित के रूप में अपने मुख्य में निम्नलिखित के रूप में एक शब्दकोश बनाया है अब जबकि डीबगिंग मुझे पता चला केवल GetHashCode विधि यह है कि जब emp1 संग्रह में जोड़ा जाता है कुंजी प्रकार के लिए कहा जाता है, उसके बाद जब emp2 संग्रह में जोड़ा जाता है तो केवल GetHashCode विधि को फिर से बुलाया जाता है लेकिन एमपी 3 के मामले में GetHashCode और Equals विधियों को कॉल किया जाता है।

हो सकता है कि यह सवाल पूछने में बहुत बेवकूफ लग रहा हो लेकिन संग्रह में eqImp2 ऑब्जेक्ट को जोड़ा जाने पर समान विधि क्यों नहीं कहा जाता है। अंदर क्या हो रहा है। कृपया समझाएँ।

+0

'emp2' और' emp3' के पास हैशकोड हैं, GetHashCode – Jehof

+0

के संभावित कार्यान्वयन के आधार पर [संभावित विधि ओवरराइड होने पर GetHashCode को ओवरराइड करना क्यों महत्वपूर्ण है?] (Http: // stackoverflow।कॉम/प्रश्न/371328/क्यों-यह-महत्वपूर्ण-टू-ओवरराइड-गेटहाशकोड-कब-बराबर-विधि-ओवर-ऑर्डर) – Richard

उत्तर

8

शब्दकोश और अन्य सभी समान कंटेनरों एक त्वरित और गंदा चेक के रूप में hashCode का उपयोग करें: अलग hashcodes मतलब है कि दो वस्तुओं निश्चित रूप से बराबर नहीं हैं; समान हैशकोड का कोई मतलब नहीं है। GetHashCode के प्रलेखन

दो वस्तुओं के रूप में बराबर की तुलना करें, प्रत्येक वस्तु के लिए GetHashCode विधि एक ही मान लौटाना चाहिए कह कर इस व्यवहार निर्दिष्ट करता है। हालांकि, यदि दो ऑब्जेक्ट्स बराबर की तुलना नहीं करते हैं, तो दो ऑब्जेक्ट्स के लिए GetHashCode विधियां को अलग-अलग मानों को वापस नहीं करना है।

आपका emp1 और emp2 अलग hashcodes उत्पन्न, इसलिए शब्दकोश Equals चलाने की आवश्यकता नहीं है; यह पहले से ही जानता है कि वे बराबर नहीं हैं। दूसरी ओर, emp2 और emp3 समान हैशकोड उत्पन्न करते हैं, इसलिए शब्दकोश को निश्चित रूप से निर्धारित करने के लिए Equals पर कॉल करना होगा, या यदि समान हैशकोड केवल मौका का परिणाम था।

+0

उत्कृष्ट उत्तर, केवल एक चीज जो मैं अभी भी उत्सुक हूं, एक डुप्लिकेट होने पर शब्दकोश की प्रतिक्रिया है जोड़ा। मुझे लगता है कि यह एक अपवाद फेंकता है? – Khan

+0

@ जेफरीखान: [यह करता है] (http://msdn.microsoft.com/en-us/library/k7z0zy8k.aspx)। – Jon

+0

@ जोन अच्छा स्पष्टीकरण के लिए बहुत बहुत धन्यवाद :) – Vikram

0

ऐसा इसलिए है क्योंकि GetHashCode एक शॉर्टकट है। सी # पहले GetHashCode पर कॉल करेगा जिसे तेजी से निष्पादित किया जाना चाहिए। यदि दो ऑब्जेक्ट्स के पास हैशकोड अलग हैं तो अनुमान लगाया जा सकता है कि, अधिक महंगा विधि के बराबर है। केवल तभी यदि उनके पास एक ही हैशकोड है तो यह को के बराबर कॉल करेगा। ऐसा इसलिए है क्योंकि हैशकोड अद्वितीय होने की गारंटी नहीं है

1

आपके उदाहरण में GetHashCode नाम हैश कोड देखता है। एमपी 3 के समान नाम emp2 है, ("विक्रम")। वे हैंश कोड के बराबर हैं इसलिए यह Equals का उपयोग कर आगे देखता है।

1

emp2 और emp3 एक ही कुंजी है। यह शब्दकोश में "कुंजी टक्कर" का कारण बन जाएगा। इसे पहले GetHashCode() कहा जाता है और निर्धारित हैश कोड समान थे। यह तब सुनिश्चित करता है कि वे बराबर() को कॉल करके समान हैं। शब्दकोश से कोड है:

int num = this.comparer.GetHashCode(key) & 2147483647; 
... 
if (this.entries[i].hashCode == num && this.comparer.Equals(this.entries[i].key, key)) 

जाहिर है, अगर hashcodes मेल नहीं खाते, यह बराबर कॉल करने के लिए है कभी नहीं।

आपको आईएलएसपी जैसे टूल मिलना चाहिए और फिर आप कोड को देख सकते हैं और अपना जवाब ढूंढ सकते हैं।

+0

मुझे नहीं पता था कि उपकरण मौजूद था - सबसे अच्छी चीज जो मैंने उम्र में देखी है, धन्यवाद! :-) – DiskJunky

1

यदि आप इस प्रयोग को जारी रखते हैं, तो आप Dictionary<TKey, TValue> कार्यान्वयन के लिए विशिष्ट कुछ व्यवहार देखेंगे, और कुछ व्यवहार जो आपके द्वारा GetHashCode लागू करने के तरीके के कारण आवश्यक हैं।

सबसे पहले, समानता के लिए वस्तुओं की तुलना करते समय GetHashCode और Equals की भूमिका को समझना महत्वपूर्ण है। अतिरिक्त जानकारी this question पर उपलब्ध है, लेकिन मैं बुनियादी नियमों यहां दोहराने की आवश्यकता होगी:

  1. Equals विधि स्थापित करता है वास्तव में जो वस्तुओं के बराबर हैं और जो ऑब्जेक्ट नहीं है। लौटने से पहले अंतिम निर्धारण के लिए इस विधि में सभी आवश्यक जांच की जानी चाहिए।
  2. हैश कोड एक वस्तु है जो आपके ऑब्जेक्ट के मूल्य से गणना की जाती है। आम तौर पर यह मूल वस्तु से बहुत छोटा है (हमारे मामले में हैश कोड एक 4 बाइट पूर्णांक है) और आवश्यक रूप से अद्वितीय नहीं है। हालांकि यह मूल वस्तुओं की तुलना में एक-दूसरे की गणना और तुलना करने के लिए बहुत तेज़ है।
    • जब हैश कोड अद्वितीय होने की जरूरत नहीं है, विभिन्न हैश कोड विभिन्न वस्तुओं (अर्थात Equals निश्चित रूप से अवास्तविक लौटाते हैं) से संकेत मिलता है, लेकिन बराबर हैश कोड का कोई मतलब है (यानी Equals सही या गलत लौट सकता है) नहीं है।

संग्रह जो एक महत्वपूर्ण वस्तु (उदा .NET में IDictionary<TKey, TValue>, या Map<K, V> जावा में) के साथ सहयोगी मूल्यों हैश कोड का लाभ लेने के कार्यान्वयन क्षमता में सुधार करने के लिए। हालांकि, चूंकि Object.GetHashCode के लिए प्रलेखन विशेष रूप से परिणामों को अद्वितीय होने की आवश्यकता नहीं है, इसलिए ये संग्रह उचित कार्यक्षमता के लिए अकेले हैश कोड पर भरोसा नहीं कर सकते हैं। जब दो ऑब्जेक्ट्स में एक ही हैश कोड होता है, तो केवल Equals पर कॉल उन्हें अलग कर सकता है। मामले आप emp3 की प्रविष्टि के लिए का वर्णन इस मामले में गिर जाता है: [IDictionary<TKey, TValue>.Add] विधि यदि आप एक ही मूल्य डालने के लिए कोशिश कर रहे हैं एक ArgumentException फेंक करने की जरूरत है, और अगर नया कुंजी emp3 बराबर है केवल Equals के लिए एक कॉल निर्धारित कर सकते हैं पहले डाले गए emp3 पर।

अतिरिक्त कार्यान्वयन विशेषताओं

विशेष संग्रह कार्यान्वयन GetHashCode के लिए और अधिक कॉल की तुलना में आप की आशा में हो सकता है। उदाहरण के लिए, जब hash table का आंतरिक संग्रहण आकार बदलता है, तो एक कार्यान्वयन संग्रह में संग्रहीत प्रत्येक ऑब्जेक्ट के लिए GetHashCode पर कॉल कर सकता है। binary- या B-tree पर आधारित संग्रह केवल GetHashCode पर कॉल कर सकते हैं (यदि परिणाम पेड़ संरचना में कैश किए जाते हैं), या प्रत्येक प्रविष्टि या लुकअप ऑपरेशन के दौरान GetHashCode को कई ऑब्जेक्ट्स के लिए कॉल करने की आवश्यकता हो सकती है (यदि परिणाम कैश नहीं किए जाते हैं)।

कभी कभी हैश तालिका कार्यान्वयन के तरीके के कारण वे "बकेट" में जगह कुंजी के सापेक्ष गणित का उपयोग करने के एक से अधिक ऑब्जेक्ट के लिए GetHashCode, या शायद अलग हैश कोड के साथ वस्तुओं के लिए भी Equals कॉल करने के लिए की जरूरत है। इसकी विशिष्ट विशेषताएं एक कार्यान्वयन से अगले में भिन्न होती हैं।