2012-08-07 14 views
5

अलोहा,HashSet <T> .RemoveWhere() और GetHashCode()

यहाँ एक सरल वर्ग कि GetHashCode ओवरराइड करता है है अपने पाठ संपत्ति बदलने के लिए, इस तरह:

var hashset = new HashSet<OverridesGetHashCode>(); 
var oghc = new OverridesGetHashCode { Text = "1" }; 
hashset.Add(oghc); 
oghc.Text = "2"; 

तो यह काम नहीं करता:

var removedCount = hashset.RemoveWhere(c => ReferenceEquals(c, oghc)); 
// fails, nothing is removed 
Assert.IsTrue(removedCount == 1); 

और न करता है:

// this line works, i.e. it does find a single item matching the predicate 
var existing = hashset.Single(c => ReferenceEquals(c, oghc)); 
// but this fails; nothing is removed again 
var removed = hashset.Remove(existing); 
Assert.IsTrue(removed); 

मैं हैश वह आंतरिक रूप से उपयोग करता है जब आइटम डाला जाता है और, कि अगर सच है, यह समझ में आता है कि hashset.Contains (oghc) काम नहीं करता है उत्पन्न होता है लगता है। मुझे यह भी लगता है कि यह अपने हैश कोड द्वारा आइटम को देखता है और यदि यह एक मैच पाता है, तो केवल यह भविष्यवाणी की जांच करता है, और यही कारण है कि पहला परीक्षण विफल रहता है (फिर से, मैं बस अनुमान लगा रहा हूं)। लेकिन अंतिम परीक्षण क्यों विफल रहता है, मुझे बस उस ऑब्जेक्ट को हैशसेट से बाहर मिला है? क्या मुझे कुछ याद आ रहा है, क्या हैशसेट से कुछ निकालने का यह गलत तरीका है?

इसे पढ़ने के लिए समय निकालने के लिए धन्यवाद।

अद्यतन: भ्रम से बचने के लिए, यहाँ बराबर() है:

protected bool Equals(OverridesGetHashCode other) 
    { 
     return string.Equals(Text, other.Text); 
    } 

public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != this.GetType()) return false; 
     return Equals((OverridesGetHashCode) obj); 
    } 
+0

आपको शायद एरिक लिपर्ट के [दिशानिर्देश और GetHashCode के नियम] (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for- gethashcode.aspx) विशेष रूप से नियम * GetHashCode द्वारा लौटाया गया पूर्णांक कभी भी परिवर्तित नहीं होना चाहिए, जबकि ऑब्जेक्ट डेटा संरचना में निहित है जो हैश कोड शेष स्थिर * पर निर्भर करता है। –

+0

मैंने पहली बार सोचा कि यह एक अच्छा सवाल था, अब मुझे लगता है जैसे मैंने कुछ सचमुच बेवकूफ पूछा :) यह सब थोड़ी देर के बाद समझ में आता है, यह पहले पहले काउंटर-सहज महसूस करता था। 'मैंने कभी भी हैशसेट का उपयोग नहीं किया' सबसे अच्छा बहाना है जिसके साथ मैं आ सकता हूं: डी धन्यवाद सब। –

उत्तर

2

यहां अच्छे उत्तर दिए गए हैं और बस इसे जोड़ना चाहते हैं। आप decompiled HashSet<T> कोड को देखें, तो आपको लगता है कि Add(value) निम्नलिखित करता देखेंगे:

  1. कॉल IEqualityComparer<T>.GetHashCode() मूल्य के लिए हैश कोड प्राप्त करने के। डिफ़ॉल्ट तुलनाकर्ता के लिए यह GetHashCode() तक उबाल जाता है।
  2. उस हैश कोड का उपयोग करता है जो गणना करने के लिए "बाल्टी" और "स्लॉट" (संदर्भ) मान को संग्रहीत किया जाना चाहिए।
  3. संदर्भ स्टोर करता है।

जब आप Remove(value) पर कॉल करते हैं तो यह चरण 1. और 2. फिर से पता चलता है कि संदर्भ कहां है। फिर यह सुनिश्चित करने के लिए कि यह वास्तव में सही मूल्य पाता है, IEqualityComparer<T>.Equals() पर कॉल करता है। हालांकि, चूंकि आपने GetHashCode() रिटर्न बदल दिया है, इसलिए यह एक अलग बाल्टी/स्लॉट स्थान की गणना करता है, जो अमान्य है। इस प्रकार, यह वस्तु नहीं मिल सकता है।

तो, ध्यान दें कि Equals() वास्तव में यहां खेल नहीं आता है, क्योंकि हैश कोड में परिवर्तन होने पर यह कभी भी सही बाल्टी/स्लॉट स्थान तक नहीं पहुंच पाएगा।

4

अपने वस्तु की हैश कोड बदलते समय है कि पिंड एक HashSet में इस्तेमाल किया जा रहा है HashSet के अनुबंध का उल्लंघन है के द्वारा।

ऑब्जेक्ट को हटाने में असमर्थ होने से समस्या यहां नहीं है। आपको पहले स्थान पर हैश कोड बदलने की अनुमति नहीं है।

मुझे MSDN से बोली:

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

वे कहानी को थोड़ा अलग बताते हैं लेकिन सार समान है। वे कहते हैं, हैश कोड कभी परिवर्तन नहीं कर सकता है। अभ्यास में, आप इसे तब तक बदल सकते हैं जब तक आप सुनिश्चित न करें कि कोई भी पुराने हैश कोड का उपयोग नहीं करता है। यह नहीं कि यह अच्छा अभ्यास है, लेकिन यह काम करता है।

+1

यह तर्क दिया जा सकता है कि ऑब्जेक्ट स्थिति में एक * संशोधन था जो ऑब्जेक्ट के बराबर विधि का रिटर्न मान निर्धारित करता है * –

+0

संपादित उद्धरण यहां एक पूरी तरह से अलग समस्या है। एक ही डेटा वाले एक ऑब्जेक्ट को एक ही हैश कोड वापस करना चाहिए, लेकिन चूंकि ऑब्जेक्ट के पास अब अलग-अलग डेटा हैं, इसलिए एक अलग हैश कोड वापस करने का अधिकार है (इसे * इसे म्यूट करने के बाद इसे वापस नहीं करना चाहिए)। – Servy

+0

@Usr "जब तक ऑब्जेक्ट स्टेटस में कोई संशोधन नहीं होता है जो ऑब्जेक्ट के बराबर के रिटर्न वैल्यू को निर्धारित करता है। संभवतः, यदि ऑब्जेक्ट की तुलना टेक्स्ट के मूल्य के आधार पर समानता के लिए की जा रही है, तो यह GetHashCode() के लिए समझ में आता है पाठ के मूल्य के आधार पर एक मूल्य वापस करने के लिए, भले ही पाठ परिवर्तन के अधीन हो। – drch

4

यह महत्वपूर्ण है कि किसी भी आइटम हैश आधारित तालिका (HashSet, Dictionary इत्यादि) में जोड़ा गया हो) संरचना में डालने के बाद संशोधित नहीं किया जा सकता है (कम से कम जब तक उन्हें हटाया नहीं जाता है)।

डेटा संरचना में कोई ऑब्जेक्ट ढूंढने के लिए यह हैश कोड की गणना करता है, और फिर उस हैश कोड के आधार पर एक स्थान पाता है।यदि आप उस ऑब्जेक्ट को म्यूट करते हैं तो हैश कोड यह उस डेटा संरचना में वर्तमान स्थान को प्रतिबिंबित नहीं करता है (जब तक कि आप बहुत भाग्यशाली नहीं होते हैं और यह केवल हैश टकराव होता है)।

MSDN page for Dictionary पर है कहते हैं:

जब तक एक वस्तु Dictionary<TKey, TValue> में एक महत्वपूर्ण के रूप में प्रयोग किया जाता है, यह अपने हैश मान प्रभावित करता है किसी भी तरह से बदल नहीं करना चाहिए।

यह वही दावा HashSet पर भी लागू होता है, क्योंकि दोनों को हैश टेबल का उपयोग करके लागू किया जाता है।

+0

हां। उपर्युक्त उदाहरण में, यदि आप हैशसेट करते हैं। RemoveWhere (x => true), यह अभी भी कुछ भी नहीं हटाएगा। भविष्यवाणी सच है, लेकिन हैशसेट ऑब्जेक्ट नहीं ढूंढ सकता है। – drch

संबंधित मुद्दे