क्या "सेट" में एक विधि होनी चाहिए?


22

चलो यह C # वर्ग है (यह जावा में लगभग समान होगा)

public class MyClass {
   public string A {get; set;}
   public string B {get; set;}

   public override bool Equals(object obj) {
        var item = obj as MyClass;

        if (item == null || this.A == null || item.A == null)
        {
            return false;
        }
        return this.A.equals(item.A);
   }

   public override int GetHashCode() {
        return A != null ? A.GetHashCode() : 0;
   }
}

जैसा कि आप देख सकते हैं, केवल MyClassपर निर्भर करता है के दो उदाहरणों की समानता A। तो दो उदाहरण हो सकते हैं जो समान हैं, लेकिन उनकी Bसंपत्ति में जानकारी के विभिन्न टुकड़े पकड़े हुए हैं।

कई भाषाओं के एक मानक संग्रह पुस्तकालय में (सी # और जावा सहित), निश्चित रूप से एक Set( HashSetसी # में) है, जो एक संग्रह है, जो समान उदाहरणों के प्रत्येक सेट से अधिकतम एक आइटम पर पकड़ सकता है।

एक आइटम जोड़ सकते हैं, आइटम निकाल सकते हैं और जांच सकते हैं कि सेट में कोई आइटम है या नहीं। लेकिन सेट से एक विशेष आइटम प्राप्त करना असंभव क्यों है?

HashSet<MyClass> mset = new HashSet<MyClass>();
mset.Add(new MyClass {A = "Hello", B = "Bye"});

//I can do this
if (mset.Contains(new MyClass {A = "Hello", B = "See you"})) {
    //something
}

//But I cannot do this, because Get does not exist!!!
MyClass item = mset.Get(new MyClass {A = "Hello", B = "See you"});
Console.WriteLine(item.B); //should print Bye

मेरे आइटम को पुनर्प्राप्त करने का एकमात्र तरीका पूरे संग्रह पर पुनरावृति करना और समानता के लिए सभी आइटम की जांच करना है। हालाँकि, O(n)इसके बजाय समय लगता है O(1)!

मुझे अब तक किसी सेट से मिलने वाली कोई भी भाषा नहीं मिली है। सभी "आम" भाषाएं जिन्हें मैं जानता हूं (जावा, सी #, पायथन, स्काला, हास्केल ...) उसी तरह से डिज़ाइन किए गए लगते हैं: आप आइटम जोड़ सकते हैं, लेकिन आप उन्हें पुनर्प्राप्त नहीं कर सकते। क्या कोई अच्छा कारण है कि ये सभी भाषाएं कुछ आसान और स्पष्ट रूप से उपयोगी का समर्थन नहीं करती हैं? वे सभी गलत नहीं हो सकते हैं, है ना? क्या कोई भाषाएँ हैं जो इसका समर्थन करती हैं? हो सकता है कि सेट से किसी खास चीज को दोबारा लेना गलत हो, लेकिन ऐसा क्यों?


कुछ संबंधित SO प्रश्न हैं:

/programming/7283338/getting-an-element-from-a-set

/programming/7760364/how-to-retrieve-actual-item-from-hashsett


12
C ++ std::setऑब्जेक्ट रिट्रीवल का समर्थन करता है, इसलिए सभी "आम" भाषाएं नहीं हैं जैसे आप वर्णन करते हैं।
मोनिका

17
यदि आप दावा करते हैं (और कोड) कि "MyClass के दो उदाहरणों की समानता केवल एक पर निर्भर करती है" तो एक और उदाहरण जिसमें समान A मान है और अलग-अलग B प्रभावी रूप से "वह विशेष उदाहरण" है, क्योंकि आप स्वयं परिभाषित करते हैं कि वे समान हैं और बी में अंतर कोई फर्क नहीं पड़ता; कंटेनर "अनुमति" के बाद से अन्य उदाहरण के बराबर है।
पीटरिस

7
सच्ची कहानी: जावा में, कई Set<E>कार्यान्वयन सिर्फ Map<E,Boolean>अंदर पर हैं।
corsiKa

10
व्यक्ति A से बात करते हुए : "हाय, क्या आप व्यक्ति को यहां ला सकते हैं कृपया"
ब्रैड थॉमस

7
यह a == bमामले में संवेदनशीलता ( हमेशा सच) को तोड़ता है this.A == nullif (item == null || this.A == null || item.A == null)परीक्षण संभवतः क्रम कृत्रिम रूप से "उच्च गुणवत्ता" कोड बनाने के लिए, बहुत करने के लिए "overdone" और चेक है। मैं इस तरह के "ओवरचेकिंग" देखता हूं और कोड रिव्यू पर हर समय अत्यधिक सही रहता हूं।
usr

जवाबों:


66

यहाँ समस्या यह नहीं है कि HashSetएक Getविधि का अभाव है , यह है कि आपका कोड HashSetप्रकार के दृष्टिकोण से कोई मतलब नहीं है ।

यह Getविधि प्रभावी रूप से है, "मुझे यह मान प्राप्त करें, कृपया", जिससे .NET फ्रेमवर्क समझदारी से उत्तर दे, "एह! आपके पास पहले से ही वह मूल्य है <confused face />"।

यदि आप वस्तुओं को स्टोर करना चाहते हैं और फिर उन्हें एक और थोड़ा अलग मूल्य के आधार पर पुनः प्राप्त करते हैं, तो फिर Dictionary<String, MyClass>जैसा आप कर सकते हैं उपयोग करें:

var mset = new Dictionary<String, MyClass>();
mset.Add("Hello", new MyClass {A = "Hello", B = "Bye"});

var item = mset["Hello"];
Console.WriteLine(item.B); // will print Bye

समाप्‍त वर्ग से समानता की सूचना लीक हो जाती है। अगर मैं इसमें शामिल संपत्तियों के सेट को बदलना चाहता था Equals, तो मुझे बाहर का कोड बदलना होगा MyClass...

ठीक है, लेकिन ऐसा इसलिए है क्योंकि MyClassकम से कम विस्मय (पीओएलए) के सिद्धांत के साथ एमोक चलता है। उस समानता कार्यक्षमता के साथ, यह मान लेना पूरी तरह से उचित है कि निम्नलिखित कोड मान्य है:

HashSet<MyClass> mset = new HashSet<MyClass>();
mset.Add(new MyClass {A = "Hello", B = "Bye"});

if (mset.Contains(new MyClass {A = "Hello", B = "See you"})) 
{
    // this code is unreachable.
}

इसे रोकने के लिए, MyClassसमानता के विषम रूप के रूप में स्पष्ट रूप से प्रलेखित होने की आवश्यकता है। ऐसा करने के बाद, यह अब समझाया नहीं जा रहा है और यह बदल रहा है कि समानता के काम खुले / बंद सिद्धांत को कैसे तोड़ देंगे। एर्गो, यह नहीं बदलना चाहिए और इसलिए Dictionary<String, MyClass>इस विषम आवश्यकता के लिए एक अच्छा समाधान है।


2
@vojta, उस स्थिति में, उपयोग करें Dictionary<MyClass, MyClass>क्योंकि यह तब उपयोग की जाने वाली कुंजी के आधार पर मूल्य प्राप्त करेगा MyClass.Equals
डेविड अरनो

8
मैं Dictionary<MyClass, MyClass>एक उपयुक्त के साथ एक आपूर्ति का उपयोग करता हूं IEqualityComparer<MyClass>, और समतुल्यता के संबंध को बाहर खींचता हूं MyClassक्यों MyClassइसके उदाहरणों पर इस संबंध के बारे में जानना पड़ता है?
कैलथ

16
@vojta और वहां की टिप्पणी: " meh। समानों के कार्यान्वयन को ओवरराइड करना ताकि गैर-बराबर ऑब्जेक्ट" बराबर "यहां समस्या हो। एक विधि के लिए पूछना जो कहता है कि" मुझे इस वस्तु के समान वस्तु प्राप्त करें ", और फिर। उम्मीद है कि एक गैर-समान वस्तु लौटा दी जाएगी, जो रखरखाव की समस्याओं का कारण बनने के लिए पागल और आसान है "पर हाजिर है। यह अक्सर एसओ के साथ समस्या है: गंभीर रूप से त्रुटिपूर्ण उत्तर लोक द्वारा उतारे जाते हैं, जिन्होंने अपने टूटे हुए कोड को जल्दी ठीक करने की इच्छा के निहितार्थ के माध्यम से नहीं सोचा है ...
डेविड अरनो

6
@DavidArno: जब तक हम एकता और पहचान के बीच अंतर करने वाली भाषाओं का उपयोग करने में लगे रहते हैं, तब तक अपरिहार्य है;; यदि आप चाहते हैं कि वस्तुओं को कैनोनिकल करें, जो समान हैं, लेकिन समान नहीं हैं, तो आपको एक ऐसी विधि की आवश्यकता है जो कहती है कि मुझे समान न मिले; इस ऑब्जेक्ट को ऑब्जेक्ट ", लेकिन" मुझे इस ऑब्जेक्ट के बराबर विहित ऑब्जेक्ट मिलता है "। जो कोई भी सोचता है कि इन भाषाओं में HashSet.Get जरूरी होगा "मुझे समान वस्तु प्राप्त करें" पहले से ही गलती से है।
स्टीव जेसोप

4
इस उत्तर में कई कंबल कथन हैं जैसे कि ...reasonable to assume...। यह सब 99% मामलों में सही हो सकता है लेकिन फिर भी एक सेट से किसी वस्तु को पुनः प्राप्त करने की क्षमता काम आ सकती है। वास्तविक विश्व कोड हमेशा POLA आदि सिद्धांतों का पालन नहीं कर सकता है। उदाहरण के लिए, यदि आप केस-असंवेदनशील तार काट रहे हैं, तो आप "मास्टर" आइटम प्राप्त करना चाहते हैं। Dictionary<string, string>एक वर्कअराउंड है, लेकिन यह संपूर्ण खर्च करता है।
usr

24

आपके पास पहले से ही आइटम है जो "सेट" में है - आपने इसे कुंजी के रूप में पारित किया है।

"लेकिन यह वह उदाहरण नहीं है जिसे मैंने ऐड कहा है" - हां, लेकिन आपने विशेष रूप से दावा किया था कि वे बराबर थे।

A Setभी एक विशेष केस है a Map| Dictionary, मूल्य प्रकार के रूप में शून्य के साथ (अच्छी तरह से बेकार तरीकों को परिभाषित नहीं किया गया है, लेकिन यह कोई फर्क नहीं पड़ता)।

आप जिस डेटा संरचना की तलाश कर रहे हैं, वह एक ऐसा स्थान है Dictionary<X, MyClass>जहां Xकिसी तरह MyClasses के रूप में As मिलता है।

सी # शब्दकोश प्रकार इस संबंध में अच्छा है, क्योंकि यह आपको कुंजी के लिए एक IEqualityComparer की आपूर्ति करने की अनुमति देता है।

दिए गए उदाहरण के लिए, मेरे पास निम्नलिखित होंगे:

public class MyClass {
   public string A {get; set;}
   public string B {get; set;}
}

public class MyClassEquivalentAs : IEqualityComparer<MyClass>{
   public override bool Equals(MyClass left, MyClass right) {
        if (Object.ReferenceEquals(left, null) && Object.ReferenceEquals(right, null))
        {
            return true;
        }
        else if (Object.ReferenceEquals(left, null) || Object.ReferenceEquals(right, null))
        {
            return false;
        }
        return left.A == right.A;
   }

   public override int GetHashCode(MyClass obj) {
        return obj?.A != null ? obj.A.GetHashCode() : 0;
   }
}

इस प्रकार उपयोग किया जाता है:

var mset = new Dictionary<MyClass, MyClass>(new MyClassEquivalentAs());
var bye = new MyClass {A = "Hello", B = "Bye"};
var seeyou = new MyClass {A = "Hello", B = "See you"};
mset.Add(bye);

if (mset.Contains(seeyou)) {
    //something
}

MyClass item = mset[seeyou];
Console.WriteLine(item.B); // prints Bye

ऐसी कई स्थितियां हैं जहां यह कोड के लिए फायदेमंद हो सकता है जिसमें कुंजी का मिलान करने वाली वस्तु है, इसे कुंजी के रूप में उपयोग की जाने वाली वस्तु के संदर्भ में बदलना है। उदाहरण के लिए, यदि कई स्ट्रिंग्स को एक हैशेड संग्रह में एक स्ट्रिंग से मिलान करने के लिए जाना जाता है, तो उन सभी स्ट्रिंग्स के संदर्भ को संग्रह में एक के संदर्भ में बदलने से प्रदर्शन जीत हो सकती है।
सुपरकैट

@ सुपरकैट आज जो एक के साथ हासिल किया गया है Dictionary<String, String>
माइकफा

@ माइकफय: हाँ, लेकिन यह प्रत्येक स्ट्रिंग संदर्भ को दो बार संग्रहीत करने के लिए थोड़ा असावधान लगता है।
सुपरकैट

2
यदि आप एक समान स्ट्रिंग का मतलब है , तो यह सिर्फ स्ट्रिंग इंटर्निंग है। निर्मित सामान का उपयोग करें। यदि आप किसी प्रकार के "कैनोनिकल" प्रतिनिधित्व (एक है कि सरल मामले को बदलने की तकनीक, आदि का उपयोग करके प्राप्त नहीं किया जा सकता है), का मतलब है कि लगता है जैसे आप मूल रूप से एक सूचकांक की जरूरत है (अर्थ में DBs शब्द का उपयोग करें)। मुझे प्रत्येक "गैर-विहित फ़ॉर्म" को एक कुंजी के रूप में संग्रहीत करने में कोई समस्या नहीं दिखाई देती है, जो एक कैनोनिकल रूप में मैप करता है। (मुझे लगता है कि यह समान रूप से अच्छी तरह से लागू होता है यदि "कैनोनिकल" फॉर्म एक स्ट्रिंग नहीं है।) यदि यह वह नहीं है जिसके बारे में आप बात कर रहे हैं, तो आप मुझे पूरी तरह से खो चुके हैं।
jpmc26

1
कस्टम Comparerऔर Dictionary<MyClass, MyClass>एक व्यावहारिक समाधान है। जावा में, समान TreeSetया TreeMapअधिक कस्टम द्वारा प्राप्त किया जा सकता है Comparator
मार्कस कल

19

आपकी समस्या यह है कि आपके पास समानता की दो विरोधाभासी अवधारणाएँ हैं:

  • वास्तविक समानता, जहां सभी क्षेत्र समान हैं
  • सदस्यता समानता सेट करें, जहां केवल A बराबर है

यदि आप अपने सेट में वास्तविक समानता संबंध का उपयोग करते हैं, तो सेट से किसी विशेष आइटम को प्राप्त करने की समस्या उत्पन्न नहीं होती है - यह जांचने के लिए कि क्या कोई वस्तु सेट में है, आपके पास पहले से ही वह वस्तु है। इसलिए एक सेट से किसी विशेष उदाहरण को प्राप्त करना कभी आवश्यक नहीं है, यह मानते हुए कि आप सही समानता संबंध का उपयोग कर रहे हैं।

हम यह भी तर्क दे सकते हैं कि एक सेट एक सार डेटा प्रकार है जिसे शुद्ध रूप से S contains xया x is-element-of Sसंबंध ("विशेषता फ़ंक्शन") द्वारा परिभाषित किया गया है । यदि आप अन्य ऑपरेशन चाहते हैं, तो आप वास्तव में सेट की तलाश में नहीं हैं।

जो अक्सर होता है - लेकिन जो सेट नहीं होता है - वह यह है कि हम सभी वस्तुओं को अलग-अलग समतुल्य वर्गों में समूहित करते हैं । ऐसे प्रत्येक वर्ग या सबसेट में वस्तुएं केवल समतुल्य होती हैं, समान नहीं। हम उस उपसमूह के किसी भी सदस्य के माध्यम से प्रत्येक तुल्यता वर्ग का प्रतिनिधित्व कर सकते हैं, और फिर उस प्रतिनिधित्व तत्व को प्राप्त करना वांछनीय हो जाता है। यह समतुल्य वर्ग से प्रतिनिधि तत्व तक मानचित्रण होगा ।

सी # में, एक शब्दकोश स्पष्ट समानता संबंध का उपयोग कर सकता है, मुझे लगता है। अन्यथा, एक त्वरित आवरण वर्ग लिखकर इस तरह के संबंध को लागू किया जा सकता है। स्यूडोकोड:

// The type you actually want to store
class MyClass { ... }

// A equivalence class of MyClass objects,
// with regards to a particular equivalence relation.
// This relation is implemented in EquivalenceClass.Equals()
class EquivalenceClass {
  public MyClass instance { get; }
  public override bool Equals(object o) { ... } // compare instance.A
  public override int GetHashCode() { ... } // hash instance.A
  public static EquivalenceClass of(MyClass o) { return new EquivalenceClass { instance = o }; }
}

// The set-like object mapping equivalence classes
// to a particular representing element.
class EquivalenceHashSet {
  private Dictionary<EquivalenceClass, MyClass> dict = ...;
  public void Add(MyClass o) { dict.Add(EquivalenceClass.of(o), o)}
  public bool Contains(MyClass o) { return dict.Contains(EquivalenceClass.of(o)); }
  public MyClass Get(MyClass o) { return dict.Get(EquivalenceClass.of(o)); }
}

"एक सेट से एक विशेष उदाहरण को पुनः प्राप्त करें" मुझे लगता है कि यह आपको बताएगा कि यदि आपने "सदस्य" को "सदस्य" में बदल दिया है तो इसका सीधा मतलब क्या है। बस एक मामूली सुझाव। =) +1
jpmc26

7

लेकिन सेट से एक विशेष आइटम प्राप्त करना असंभव क्यों है?

क्योंकि वह सेट किस लिए नहीं है।

उदाहरण के लिए मुझे पुनः बताएं।

"मेरे पास एक HashSet है जिसे मैं MyClass ऑब्जेक्ट्स को स्टोर करना चाहता हूं और मैं संपत्ति A का उपयोग करके उन्हें प्राप्त करने में सक्षम होना चाहता हूं जो ऑब्जेक्ट की संपत्ति A के बराबर है"।

अगर "HashSet" को "Collection", "ऑब्जेक्ट्स" को "Values" और "Property A" को "Key" से बदल दिया जाए, तो वाक्य बन जाता है:

"मेरे पास एक संग्रह है जो मैं MyClass मान को संग्रहीत करना चाहता हूं और मैं उस कुंजी का उपयोग करके उन्हें प्राप्त करने में सक्षम होना चाहता हूं जो ऑब्जेक्ट की कुंजी के बराबर है"।

जो वर्णन किया जा रहा है वह एक शब्दकोश है। जो वास्तविक प्रश्न पूछा जा रहा है, वह हैशसेट को एक शब्दकोश के रूप में क्यों नहीं माना जा सकता?

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

ओओपी और समानता टिप्पणियों / उत्तरों पर एक नोट। मैपिंग की कुंजी एक शब्दकोश में संग्रहीत मूल्य की संपत्ति / सदस्य होना ठीक है। उदाहरण के लिए: कुंजी के रूप में एक मार्गदर्शक होना और समान गुण के लिए उपयोग की जाने वाली संपत्ति पूरी तरह से उचित है। जो उचित नहीं है वह बाकी संपत्तियों के लिए अलग-अलग मूल्य रखता है। मुझे लगता है कि अगर मैं उस दिशा में जा रहा हूं, तो मुझे शायद अपनी कक्षा की संरचना पर फिर से विचार करना होगा।


6

जैसे ही आप ओवरराइड करते हैं, आप बेहतर हैशकोड को बराबर करते हैं। जैसे ही आपने यह किया है आपके "उदाहरण" को फिर से आंतरिक स्थिति को कभी नहीं बदलना चाहिए।

यदि आप समतुल्य को ओवरराइड नहीं करते हैं और हैशकोड VM ऑब्जेक्ट पहचान का उपयोग समानता निर्धारित करने के लिए किया जाता है। यदि आप इस ऑब्जेक्ट को एक सेट में रखते हैं तो आप इसे फिर से पा सकते हैं।

समानता को निर्धारित करने के लिए उपयोग की जाने वाली किसी वस्तु का मूल्य बदलने से हैश आधारित संरचनाओं में इस वस्तु की अनैच्छिकता हो जाएगी।

तो ए पर सेटर खतरनाक है।

अब आपके पास B नहीं है जो समानता में भाग नहीं लेता है। यहाँ समस्या तकनीकी रूप से नहीं है। क्योंकि तकनीकी रूप से बी बदलना समानता के तथ्य के प्रति तटस्थ है। शब्दार्थ B को "संस्करण" ध्वज जैसा कुछ होना चाहिए।

मुद्दा ये है:

यदि आपके पास दो वस्तुएं हैं जो A के समान हैं लेकिन B की आपके पास यह धारणा नहीं है कि इनमें से एक वस्तु अन्य की तुलना में नई है। यदि B के पास कोई संस्करण जानकारी नहीं है, तो यह धारणा आपके एल्गोरिथ्म में छिपी हुई है, जब आप किसी सेट में इस ऑब्जेक्ट को "अधिलेखित / अद्यतन" करने का निर्णय लेते हैं। यह स्रोत कोड स्थान जहां ऐसा होता है, स्पष्ट नहीं हो सकता है, इसलिए किसी डेवलपर के पास ऑब्जेक्ट X और ऑब्जेक्ट Y के बीच के संबंध को पहचानने के लिए कठिन समय होगा जो X से B में भिन्न होता है।

यदि बी में संस्करण की जानकारी है, तो आप इस धारणा को उजागर करते हैं कि पहले केवल कोड से अनुमानित रूप से व्युत्पन्न था। अब आप देख सकते हैं, कि वस्तु Y, X का एक नया संस्करण है।

अपने बारे में सोचें: आपकी पहचान आपका पूरा जीवन बनी रहती है, हो सकता है कि कुछ गुण बदल जाएं (जैसे आपके बालों का रंग; ;-))। यकीन है कि आप मान सकते हैं कि अगर आपके पास दो तस्वीरें हैं, एक भूरे बालों के साथ एक है, तो आप भूरे बालों के साथ फोटो पर छोटे हो सकते हैं। लेकिन शायद आपने अपने बालों को रंग दिया है? समस्या यह है: आप जान सकते हैं कि आपने अपने बालों को रंग दिया है। दूसरों को हो सकता है? इसे एक वैध संदर्भ में रखने के लिए आपको संपत्ति की आयु (संस्करण) पेश करनी होगी। तब आप शब्दार्थ से स्पष्ट और असंदिग्ध हैं।

छिपे हुए ऑपरेशन से बचने के लिए "नए ऑब्जेक्ट के खिलाफ पुराने की जगह" एक सेट में एक विधि नहीं होनी चाहिए। यदि आप इस तरह का व्यवहार चाहते हैं, तो आपको पुरानी वस्तु को हटाकर और नई वस्तु को जोड़कर इसे स्पष्ट करना होगा।

BTW: इसका क्या मतलब है अगर आप एक वस्तु में गुजरते हैं जो उस वस्तु के बराबर है जिसे आप प्राप्त करना चाहते हैं? इसका कोई मतलब नहीं निकलता। अपने शब्दार्थ को साफ रखें और ऐसा न करें हालांकि तकनीकी रूप से कोई भी आपको बाधा नहीं देगा।


7
"जैसे ही आप ओवरराइड करते हैं, आपके पास हैशकोड से बेहतर ओवरराइड हो जाता है। जैसे ही आपने यह किया है" आपका "उदाहरण फिर से आंतरिक स्थिति को नहीं बदलना चाहिए।" वह कथन +100 के लायक है, वहीं।
डेविड अरनो

परस्पर स्थिति के आधार पर समानता और हैशकोड के खतरों को इंगित करने के लिए +1
हल्क

3

विशेष रूप से जावा में, HashSetशुरू में HashMapवैसे भी लागू किया गया था , और सिर्फ मूल्य की अनदेखी की गई थी। तो आरंभिक डिजाइन ने किसी भी तरीके को प्रदान करने के लिए किसी भी लाभ का अनुमान नहीं लगाया था HashSet। यदि आप विभिन्न वस्तुओं के बीच एक विहित मूल्य को संग्रहीत और पुनः प्राप्त करना चाहते हैं, तो आप बस एक का उपयोग HashMapकरते हैं।

मैंने इस तरह के कार्यान्वयन के विवरण के साथ तारीख तक नहीं रखी है, इसलिए मैं यह नहीं कह सकता कि क्या यह तर्क अभी भी जावा में पूर्ण रूप से लागू होता है, अकेले C # आदि में लागू होता है, लेकिन भले ही इसे किसी भी मामले में HashSetकम मेमोरी का उपयोग करने के लिए फिर से लागू HashMapकिया गया हो। Setइंटरफ़ेस में एक नई विधि जोड़ने के लिए एक ब्रेकिंग परिवर्तन होगा । इसलिए यह एक बहुत ही दर्द है जो हर किसी के लायक नहीं होता है।


खैर, जावा में इसे गैर-ब्रेकिंग तरीके से करने के लिए -implementation प्रदान करना संभव हो सकता है default। यह सिर्फ एक बहुत उपयोगी परिवर्तन नहीं लगता है।
हल्क

@ हेल: मैं गलत हो सकता हूं, लेकिन मुझे लगता है कि कोई भी डिफ़ॉल्ट क्रियान्वयन जघन्य रूप से अक्षम होगा, क्योंकि प्रश्नकर्ता कहता है, "मेरे आइटम को पुनर्प्राप्त करने का एकमात्र तरीका पूरे संग्रह पर पुनरावृति करना और समानता के लिए सभी आइटमों की जांच करना है"। इतना अच्छा बिंदु, आप इसे एक पिछड़े-संगत तरीके से कर सकते हैं, लेकिन एक गोच को जोड़ने से परिणामी फ़ंक्शन केवल O(n)तुलनात्मक रूप से चलाने की गारंटी देता है, भले ही हैश फ़ंक्शन अच्छा वितरण दे रहा हो। तब के कार्यान्वयनSet उस के कार्यान्वयन इंटरफ़ेस में डिफ़ॉल्ट कार्यान्वयन को ओवरराइड करते हैं, जिसमें शामिल है HashSet, बेहतर गारंटी दे सकता है।
स्टीव जेसप

सहमत - मुझे नहीं लगता कि यह एक अच्छा विचार होगा। इस तरह के व्यवहार के लिए पूर्ववर्ती प्रक्रियाएँ होंगी - हाल ही में सूचीबद्ध सूची को जोड़ने के लिए एक डिफ़ॉल्ट कार्यान्वयन चुनने के लिए - List.get (int index) या । इंटरफ़ेस द्वारा अधिकतम जटिलता गारंटी प्रदान की जाती है, लेकिन कुछ कार्यान्वयन दूसरों की तुलना में बहुत बेहतर कर सकते हैं।
हल्क

2

एक प्रमुख भाषा है जिसके सेट में वह संपत्ति है जो आप चाहते हैं।

C ++ में, std::setएक ऑर्डर किया गया सेट है। इसमें एक .findविधि है जो आपके द्वारा प्रदान किए जाने वाले ऑर्डर करने वाले ऑपरेटर <या बाइनरी bool(T,T)फ़ंक्शन के आधार पर तत्व की तलाश करता है । आप इच्छित ऑपरेशन को लागू करने के लिए खोज का उपयोग कर सकते हैं।

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

यह एक कुशल अनुमति देता है:

std::set< std::string, my_string_compare > strings;
strings.find( 7 );

जहां my_string_compareसमझता है कि पूर्णांक को एक स्ट्रिंग (संभावित लागत पर) में परिवर्तित किए बिना पूर्णांकों और तारों को कैसे ऑर्डर किया जाए।

के लिए unordered_set(C ++ का हैश सेट), कोई समान पारदर्शी ध्वज (अभी तक) नहीं है। आपको Tएक unordered_set<T>.findविधि में पास होना चाहिए । इसे जोड़ा जा सकता है, लेकिन हैश की आवश्यकता होती है ==और एक हैशर को आदेश दिए गए सेटों के विपरीत, जिन्हें केवल एक ऑर्डर की आवश्यकता होती है।

सामान्य पैटर्न यह है कि कंटेनर लुकअप करेगा, फिर आपको कंटेनर के भीतर उस तत्व के लिए "पुनरावृत्ति" देगा। किस बिंदु पर आप सेट के भीतर तत्व प्राप्त कर सकते हैं, या इसे हटा सकते हैं, आदि।

संक्षेप में, सभी भाषाओं के मानक कंटेनरों में आपके द्वारा वर्णित दोष नहीं हैं। C ++ मानक लाइब्रेरी के इट्रेटर-आधारित कंटेनर नहीं हैं, और कम से कम कुछ कंटेनरों में आपके द्वारा वर्णित किसी भी अन्य भाषा से पहले मौजूद थे, और आपके द्वारा वर्णित किए गए तरीकों से भी अधिक कुशलता से प्राप्त करने की क्षमता । आपके डिज़ाइन में कुछ भी गड़बड़ नहीं है, या वह ऑपरेशन नहीं चाहता है; आपके द्वारा उपयोग किए जा रहे सेट के डिज़ाइनर ने वह इंटरफ़ेस प्रदान नहीं किया है।

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

यह संभव है कि तथ्य यह है कि सी ++ में यह दोष नहीं है डिजाइन का एक दुर्घटना है। इटरेटर-केंद्रित पथ का अर्थ है कि एक सहयोगी कंटेनर में एक आइटम के साथ बातचीत करने के लिए आपको पहले तत्व के लिए एक इटरेटर मिलता है, फिर आप उस इटरेटर का उपयोग कंटेनर में प्रवेश के बारे में बात करने के लिए करते हैं।

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

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