2012-01-21 15 views
81

मैं एक वर्ग है कि है IComparable है:हैशसेट समानता के तत्वों की तुलना कैसे करता है?

public class a : IComparable 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public a(int id) 
    { 
     this.Id = id; 
    } 

    public int CompareTo(object obj) 
    { 
     return this.Id.CompareTo(((a)obj).Id); 
    } 
} 

जब मैं एक हैश सेट करने के लिए इस वर्ग की वस्तु की एक सूची में जोड़ने:

a a1 = new a(1); 
a a2 = new a(2); 
HashSet<a> ha = new HashSet<a>(); 
ha.add(a1); 
ha.add(a2); 
ha.add(a1); 

सब कुछ ठीक है और ha.count2 है, लेकिन:

a a1 = new a(1); 
a a2 = new a(2); 
HashSet<a> ha = new HashSet<a>(); 
ha.add(a1); 
ha.add(a2); 
ha.add(new a(1)); 

अब ha.count3 है।

  1. क्यों नहीं HashSet सम्मान a के CompareTo विधि है।
  2. HashSet अद्वितीय वस्तुओं की सूची रखने का सबसे अच्छा तरीका है?
+0

निर्माता में 'IEqualityComparer ' के एक कार्यान्वयन जोड़ें या वर्ग 'A' में कार्यान्वित करें। https://msdn.microsoft.com/en-us/library/bb301504(v=vs.110).aspx – Jaider

उत्तर

82

यह IEqualityComparer<T> (EqualityComparer<T>.Default का उपयोग करता है जब तक कि आप निर्माण पर एक अलग निर्दिष्ट नहीं करते)।

जब आप सेट में कोई तत्व जोड़ते हैं, तो उसे IEqualityComparer<T>.GetHashCode का उपयोग करके हैश कोड मिलेगा, और हैश कोड और तत्व दोनों को स्टोर करेगा (यह जांचने के बाद कि तत्व पहले से ही सेट में है या नहीं)।

तत्व को देखने के लिए, यह पहले हैश कोड खोजने के लिए IEqualityComparer<T>.GetHashCode का उपयोग करेगा, फिर उसी हैश कोड वाले सभी तत्वों के लिए, यह वास्तविक समानता की तुलना करने के लिए IEqualityComparer<T>.Equals का उपयोग करेगा।

इसका मतलब है कि आप दो विकल्प हैं:

  • निर्माता में एक कस्टम IEqualityComparer<T> दर्रा। यह सबसे अच्छा विकल्प है यदि आप T को स्वयं संशोधित नहीं कर सकते हैं, या यदि आप एक गैर-डिफ़ॉल्ट समानता संबंध चाहते हैं (उदा। "नकारात्मक उपयोगकर्ता आईडी वाले सभी उपयोगकर्ता बराबर मानते हैं")। यह लगभग खुद को कभी भी लागू नहीं किया गया है (यानी FooIEqualityComparer<Foo> लागू नहीं करता है) लेकिन एक अलग प्रकार में जिसका उपयोग केवल तुलना के लिए किया जाता है।
  • GetHashCode और Equals(object) ओवरराइड करके, प्रकार में समानता लागू करें। आदर्श रूप में, IEquatable<T> को प्रकार में भी लागू करें, खासकर यदि यह एक मान प्रकार है। इन विधियों को डिफ़ॉल्ट समानता तुलनाकर्ता द्वारा बुलाया जाएगा।

नोट कैसे इस में से कोई भी एक आदेश दिया तुलना के संदर्भ में है - जो समझ में आता है, के रूप में निश्चित रूप से स्थितियों में, जहां आप आसानी से समानता नहीं बल्कि कुल आदेश निर्दिष्ट कर सकते हैं कर रहे हैं। यह मूल रूप से Dictionary<TKey, TValue> जैसा ही है।जो आपको एक IEqualityComparer<T> के बजाय एक IComparer<T> निर्दिष्ट करने के लिए अनुमति देता है -

आप एक सेट जो का उपयोग करता है बस समानता तुलना की बजाय आदेश देने चाहते हैं, आप नेट 4 से SortedSet<T> उपयोग करना चाहिए। यह IComparer<T>.Compare का उपयोग करेगा - यदि आप Comparer<T>.Default का उपयोग कर रहे हैं तो IComparable<T>.CompareTo या IComparable.CompareTo पर प्रतिनिधि होंगे। अपने HashSet<T> की ऑब्जेक्ट प्रकार IEqualityComparer<T> लागू करने के लिए नहीं है, लेकिन इसके बजाय सिर्फ Object.GetHashCode() और Object.Equals(Object obj) ओवरराइड करने के लिए है:

+6

+1 @ टायरकर के उत्तर को भी नोट करें (कि आईएमओ यहां एक टिप्पणी होनी चाहिए) जो बताती है कि सबसे सरल तरीका लीवरेज के लिए 'IEqualityComparer .GetHashCode/Equals()' 'समानता 'और' GetHashCode' को 'टी' पर लागू करना है (और जब आप ऐसा कर रहे हैं, तो आप दृढ़ता से टाइप किए गए समकक्ष को भी लागू करेंगे: - 'bool IEquatable ईक्वाल्स (टी अन्य) ') –

+4

हालांकि यह उत्तर कुछ हद तक भ्रमित हो सकता है, खासकर नए उपयोगकर्ताओं के लिए क्योंकि यह स्पष्ट रूप से यह नहीं बताता है कि 'बराबर' और 'गेटहाशकोड' ओवरराइड करने के सबसे सरल मामले के लिए पर्याप्त है - जैसा कि बताया गया है @ tyriker के जवाब में। – BartoszKP

+0

इमो एक बार जब आप 'आईसीओम्पेरबल' (या उस मामले के लिए 'आईसीओएमपेयर' लागू करते हैं तो आपको समानता को अलग से लागू करने के लिए नहीं कहा जाना चाहिए (लेकिन केवल 'गेटहाशकोड')। एक अर्थ में तुलनात्मकता इंटरफेस समानता इंटरफेस से प्राप्त होना चाहिए। मैं दो अलग-अलग कार्यों में प्रदर्शन लाभों को समझता हूं (जहां आप कुछ समान हैं या नहीं, यह कहकर अलग-अलग समानता को अनुकूलित कर सकते हैं) लेकिन फिर भी .. जब आप निर्दिष्ट करते हैं तो उदाहरण बहुत ही भ्रमित होते हैं जब उदाहरण 'तुलनात्मक' फ़ंक्शन में बराबर होते हैं और ढांचे पर विचार नहीं करेंगे। – nawfal

9

HashSetEquals और GetHashCode() का उपयोग करता है।

CompareTo आदेशित सेट के लिए है।

यदि आप अद्वितीय वस्तुओं चाहते हैं, लेकिन आपको उनके पुनरावृत्ति आदेश की परवाह नहीं है, HashSet<T> आमतौर पर सबसे अच्छा विकल्प है।

61

यहाँ जवाब का एक हिस्सा है कि अनकहा रह गया है पर स्पष्टीकरण है।

इसके बजाय इस बात का

:

public class a : IEqualityComparer<a> 
{ 
    public int GetHashCode(a obj) { /* Implementation */ } 
    public bool Equals(a obj1, a obj2) { /* Implementation */ } 
} 

आप ऐसा करते हैं:

public class a 
{ 
    public override int GetHashCode() { /* Implementation */ } 
    public override bool Equals(object obj) { /* Implementation */ } 
} 

यह सूक्ष्म है, लेकिन यह मेरे HashSet तरह से कार्य करने के लिए प्राप्त करने की कोशिश तक फिसल गया एक दिन के बेहतर भाग के लिए यह इरादा है। और दूसरों की तरह कहा है, HashSet<a> सेट के साथ काम करते समय a.GetHashCode() और a.Equals(obj) को कॉल करना समाप्त कर देगा।

+2

अच्छा बिंदु। @ जॉनस्केट के उत्तर पर मेरी टिप्पणी पर उल्लिखित बीटीडब्लू, आपको थोड़ी दक्षता लाभ के लिए 'बूल आईक्वेटेबल ईक्वाल्स (टी अन्य)' को भी लागू करना चाहिए, लेकिन स्पष्टता से अधिक लाभ। ओवीवी कारणों के लिए, 'Iquatable ' के साथ 'गेटहाशकोड' को लागू करने की आवश्यकता के अतिरिक्त, IEquatable के लिए दस्तावेज़ का उल्लेख है कि स्थिरता उद्देश्यों के लिए आपको –

+0

स्थिरता के लिए 'ऑब्जेक्ट' एक्वाल्स 'को ओवरराइड करना चाहिए। मैंने इसे कार्यान्वित करने का प्रयास किया। 'Ovveride getHashcode' काम करता है, लेकिन 'ऊन बराबर' ओवरराइड त्रुटि प्राप्त करता है: ओवरराइड करने के लिए कोई विधि नहीं मिली है। कोई उपाय? – Stefanvds

+1

संभोग, 'ओबीजे' होना चाहिए 'ऑब्जेक्ट obj' – Stefanvds

1

कन्स्ट्रक्टर हैशसेट ऑब्जेक्ट प्राप्त करता है जो नई ऑब्जेक्ट जोड़ने के लिए IEqualityComparer को लागू करता है। अगर HashSet में आप whant उपयोग विधि आप nead ओवरराइड इसके बराबर है, GetHashCode

namespace HashSet 
{ 
    public class Employe 
    { 
     public Employe() { 
     } 

     public string Name { get; set; } 

     public override string ToString() { 
      return Name; 
     } 

     public override bool Equals(object obj) { 
      return this.Name.Equals(((Employe)obj).Name); 
     } 

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

    class EmployeComparer : IEqualityComparer<Employe> 
    { 
     public bool Equals(Employe x, Employe y) 
     { 
      return x.Name.Trim().ToLower().Equals(y.Name.Trim().ToLower()); 
     } 

     public int GetHashCode(Employe obj) 
     { 
      return obj.Name.GetHashCode(); 
     } 
    } 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      HashSet<Employe> hashSet = new HashSet<Employe>(new EmployeComparer()); 
      hashSet.Add(new Employe() { Name = "Nik" }); 
      hashSet.Add(new Employe() { Name = "Rob" }); 
      hashSet.Add(new Employe() { Name = "Joe" }); 
      Display(hashSet); 
      hashSet.Add(new Employe() { Name = "Rob" }); 
      Display(hashSet); 

      HashSet<Employe> hashSetB = new HashSet<Employe>(new EmployeComparer()); 
      hashSetB.Add(new Employe() { Name = "Max" }); 
      hashSetB.Add(new Employe() { Name = "Solomon" }); 
      hashSetB.Add(new Employe() { Name = "Werter" }); 
      hashSetB.Add(new Employe() { Name = "Rob" }); 
      Display(hashSetB); 

      var union = hashSet.Union<Employe>(hashSetB).ToList(); 
      Display(union); 
      var inter = hashSet.Intersect<Employe>(hashSetB).ToList(); 
      Display(inter); 
      var except = hashSet.Except<Employe>(hashSetB).ToList(); 
      Display(except); 

      Console.ReadKey(); 
     } 

     static void Display(HashSet<Employe> hashSet) 
     { 
      if (hashSet.Count == 0) 
      { 
       Console.Write("Collection is Empty"); 
       return; 
      } 
      foreach (var item in hashSet) 
      { 
       Console.Write("{0}, ", item); 
      } 
      Console.Write("\n"); 
     } 

     static void Display(List<Employe> list) 
     { 
      if (list.Count == 0) 
      { 
       Console.WriteLine("Collection is Empty"); 
       return; 
      } 
      foreach (var item in list) 
      { 
       Console.Write("{0}, ", item); 
      } 
      Console.Write("\n"); 
     } 
    } 
} 
संबंधित मुद्दे