2012-10-28 4 views
8

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

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

यहाँ कोड है जो इस दर्शाता है:

public static void main(String[] args) 
    { 
     HashSet<GraphEdge> set = new HashSet<>(); 
     GraphEdge edge1 = new GraphEdge(1, "a"); 
     GraphEdge edge2 = new GraphEdge(2, "b"); 
     GraphEdge edge3 = new GraphEdge(3, "c"); 

     set.add(edge1); 
     set.add(edge2); 
     set.add(edge3); 

     edge2.setId(1); 
     edge2.setName("a"); 

     for(GraphEdge edge: set) 
     { 
      System.out.println(edge.toString()); 
     } 

     if(edge2.equals(edge1)) 
     { 
      System.out.println("Equals"); 
     } 
     else 
     { 
      System.out.println("Not Equals"); 
     } 
    } 

    public class GraphEdge 
    { 
     private int id; 
     private String name; 

     //Constructor ... 

     //Getters & Setters... 

     public int hashCode() 
     { 
     int hash = 7; 
     hash = 47 * hash + this.id; 
     hash = 47 * hash + Objects.hashCode(this.name); 
     return hash;  
     } 

     public boolean equals(Object o) 
     { 
      if(o == this) 
      { 
       return true; 
      } 

      if(o instanceof GraphEdge) 
      { 
       GraphEdge anotherGraphEdge = (GraphEdge) o; 
       if(anotherGraphEdge.getId() == this.id && anotherGraphEdge.getName().equals(this.name)) 
       { 
        return true; 
       } 
      } 

       return false; 
     } 
    } 

ऊपर कोड से उत्पादन:

1 a 
1 a 
3 c 
Equals 

वहाँ सामग्री को मान्य करने HashSet मजबूर करने के लिए एक रास्ता है ताकि संभव डुप्लिकेट प्रविष्टियों उपर्युक्त परिदृश्य में बनाया गया हटा दिया गया है?

एक नया समाधान हैशसेट बनाने के लिए एक संभावित समाधान हो सकता है और सामग्री को एक हैशसेट से दूसरे में कॉपी कर सकता है ताकि नए हैशसेट में डुप्लिकेट न हों, हालांकि मुझे यह समाधान पसंद नहीं है।

उत्तर

16

आपके द्वारा वर्णित स्थिति अमान्य है। Javadoc देखें: "किसी ऑब्जेक्ट का मान इस तरह से बदला जाता है कि ऑब्जेक्ट का मान तुलनात्मक रूप से तुलना करता है जब ऑब्जेक्ट सेट में एक तत्व होता है।"

+0

ठीक है तो उपरोक्त परिदृश्य अमान्य है। मुझे लगता है कि सामग्री को एक नए हैशसेट में कॉपी करना एकमात्र विकल्प है। –

+4

@ Spi1988 सही समाधान 'सेट' के अनुबंध से चिपकना है और संग्रह में जोड़ने के बाद वस्तुओं को संशोधित नहीं करना है। – EJP

+0

@PB_MLT सामग्री को नए हैशसेट में कॉपी करके आप क्या हासिल करेंगे? – HungryForKnowledge

-1

ऑब्जेक्ट्स हैशकोड का उपयोग पैरामीटर ऑब्जेक्ट्स का उपयोग करके एक हैकोड उत्पन्न करने के लिए किया जाता है। आप इसे हैकोड गणना के हिस्से के रूप में उपयोग कर रहे हैं।

निम्नलिखित के साथ hashCode अपने क्रियान्वयन की जगह का प्रयास करें:

public int hashCode() 
{ 
    return Objects.hashCode(this.id, this.name); 
} 
+0

ऑब्जेक्ट्सशैशकोड (this.id, this.name) मान्य नहीं है क्योंकि हैशकोड विधि केवल एक ऑब्जेक्ट लेती है। –

+0

मुझे लगता है कि आप Google संग्रह लाइब्रेरी का उपयोग कर रहे थे: –

+0

http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Objects.html#hashCode(java.lang.Object। ..) –

1

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

यदि आप इस मुद्दे से बहुत चिंतित हैं, तो शायद आपको अपने डेटा संरचनाओं पर पुनर्विचार करना होगा। उदाहरण के लिए आप अपरिवर्तनीय वस्तुओं का उपयोग कर सकते हैं। अपरिवर्तनीय वस्तुओं के साथ आपको यह समस्या नहीं होगी।

1

HashSet ऑब्जेक्ट जोड़े जाने के बाद इसके सदस्य के गुणों को बदलने के बारे में पता नहीं है। यदि यह आपके लिए एक समस्या है, तो आप GraphEdge अपरिवर्तनीय बनाने पर विचार करना चाहेंगे।

GraphEdge edge4 = edge2.changeName("new_name"); 

मामले में जहां GraphEdge अपरिवर्तनीय है, एक नया उदाहरण लौटने के बजाय मौजूदा उदाहरण बदलने में एक मूल्य के परिणाम को बदलने में: उदाहरण के लिए।

-1

आपको अपनी सूची को पुन: सक्रिय करने के समय आपको अनूठा पहचान करने की आवश्यकता होगी। एक नया HashSet बनाने जाने के लिए सही तरीके से नहीं लग सकता है, लेकिन क्यों यह कोशिश नहीं की ... और शायद नहीं एक HashSet का उपयोग के साथ शुरू करने के लिए ...

public class TestIterator { 
    public static void main(String[] args) { 
     List<String> list = new ArrayList<String>(); 

     list.add("1"); 
     list.add("1"); 
     list.add("2"); 
     list.add("3"); 

     for (String s : new UniqueIterator<String>(list)) { 
      System.out.println(s); 
     } 
    } 
} 

public class UniqueIterator<T> implements Iterable<T> { 
    private Set<T> hashSet = new HashSet<T>(); 

    public UniqueIterator(Iterable<T> iterable) { 
     for (T t : iterable) { 
      hashSet.add(t); 
     } 
    } 

    public Iterator<T> iterator() { 
     return hashSet.iterator(); 
    } 
} 
+0

उनके पास एक सूची नहीं है। उसके पास एक सेट है वह इसका दुरुपयोग कर रहा है। कोई जवाब नहीं – EJP

+0

वह एक सूची के रूप में एक सेट का उपयोग कर रहा है। तो उसे सेट को सही तरीके से उपयोग करने या सूची का उपयोग करने की आवश्यकता है। – slipperyseal

+0

वह एक सूची नहीं चाहता है। वह एक सेट चाहता है। उसके पास एक सेट है वह इसका दुरुपयोग कर रहा है, और फिर सोच रहा है कि इसके तत्व अद्वितीय क्यों नहीं हैं। समाधान इसे और खराब नहीं करना है, लेकिन इसे पहले स्थान पर रोकना बंद करना है। – EJP

3

@ EJP के जवाब देने के लिए जोड़ने के लिए, क्या होगा अभ्यास में यदि आप डुप्लीकेट बनाने के लिए HashSet में ऑब्जेक्ट्स को म्यूटेट करते हैं (equals/hashcode अनुबंध के अर्थ में) हैश तालिका डेटा संरचना टूट जाएगी।

  • उत्परिवर्तन का सटीक विवरण के आधार पर, और हैश तालिका, एक या उदाहरणों में से दोनों की स्थिति (जैसे contains और अन्य कार्यों) देखने के लिए अदृश्य हो जाएगा। या तो यह गलत हैश श्रृंखला पर है, या क्योंकि अन्य उदाहरण हैश श्रृंखला पर इससे पहले प्रकट होता है। और भविष्यवाणी करना मुश्किल है कि कौन सा उदाहरण दिखाई देगा ... और क्या यह दिखाई देगा।

  • यदि आप सेट को फिर से सेट करते हैं, तो दोनों उदाहरण अभी भी मौजूद होंगे ... Set अनुबंध का उल्लंघन करते हुए।

बेशक, यह एप्लिकेशन परिप्रेक्ष्य से बहुत टूटा हुआ है।


आप या तो द्वारा इस समस्या से बचने कर सकते हैं: अपने निर्धारित तत्वों के लिए अपरिवर्तनीय प्रकार का उपयोग

  • ,
  • वस्तुओं की एक प्रतिलिपि बनाने के रूप में आप उन्हें सेट में डाल दिया और/या खींच उन्हें सेट से बाहर,
  • है कि यह "जानता" इसलिए अवधि के लिए वस्तुओं को बदलने के लिए नहीं अपने कोड लिखने ...

शुद्धता और मजबूती के परिप्रेक्ष्य से, पहला विकल्प स्पष्ट रूप से सर्वोत्तम है।


संयोग से, इसे सामान्य रूप से "ठीक" करना वाकई मुश्किल होगा। जानने के लिए जावा में कोई व्यापक तंत्र नहीं है ... या अधिसूचित किया जा रहा है ... कि कुछ तत्व बदल गया है। आप कक्षा के आधार पर कक्षा पर इस तरह के एक तंत्र को लागू कर सकते हैं, लेकिन इसे स्पष्ट रूप से कोडित किया जाना चाहिए (और यह सस्ता नहीं होगा)। यहां तक ​​कि यदि आपके पास ऐसा तंत्र था, तो आप क्या करेंगे? स्पष्ट रूप से वस्तुओं में से एक को सेट से हटा दिया जाना चाहिए ... लेकिन कौन सा?

+0

Thx।यदि आपके पास एक तंत्र था जो यह पता लगा सकता था कि एक सेट में कोई ऑब्जेक्ट बदल गया है, और अब एक ही सेट में मौजूद किसी अन्य ऑब्जेक्ट के बराबर है, तो आप डुप्लिकेट में से किसी एक को हटा सकते हैं (इससे कोई फ़र्क नहीं पड़ता कि आप किससे हटाते हैं वे बराबर हैं)। –

+0

@ स्पि 1 9 88 - * "इससे कोई फर्क नहीं पड़ता कि आप किसके बराबर हैं क्योंकि वे बराबर हैं" *। यह सामान्य रूप से सच नहीं है। दो ऑब्जेक्ट्स जिनके लिए 'बराबर() 'वापसी' सत्य 'समान नहीं है। और यह महत्वपूर्ण हो सकता है कि आप जो भी छोड़ दें। और आपके द्वारा उत्पन्न तंत्र काल्पनिक है। –

+0

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

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