2013-06-20 10 views
6

में मैं इस सवाल पढ़ा है: Changing the elements in a set changes the 'equals' semanticsबदलने मूल्यों HashSet

हालांकि, मैं कैसे समस्या यह है कि मैं HashSet में एक आइटम को बदल नहीं सकते और इसे बाद में हटा दें हल करने के लिए पता नहीं है।

मैं कुछ उदाहरण sourcecode है:

public static void main(String[] args) { 
    TestClass testElement = new TestClass("1"); 
    Set<TestClass> set = new HashSet<>(); 
    set.add(testElement); 
    printIt(testElement, set, "First Set"); 
    testElement.setS1("asdf"); 
    printIt(testElement, set, "Set after changing value"); 
    set.remove(testElement); 
    printIt(testElement, set, "Set after trying to remove value"); 
    testElement.setS1("1"); 
    printIt(testElement, set, "Set after changing value back"); 
    set.remove(testElement); 
    printIt(testElement, set, "Set removing value"); 
} 

private static void printIt(TestClass hullo, Set<TestClass> set, String message) { 
    System.out.println(message + " (hashCode is " + hullo.hashCode() + "):"); 
    for (TestClass testClass : set) { 
     System.out.println(" " + testClass.toString()); 
     System.out.println("  HashCode: " + testClass.hashCode()); 
     System.out.println("  Element is equal: " + hullo.equals(testClass)); 
    } 
} 

कहाँ TestClass सिर्फ एक POJO कि एक चर (प्लस गेटर & सेटर) रखती है और hashCode() और बराबरी() लागू किया है।

बराबर() और हैशकोड() - विधियों को दिखाने का अनुरोध था। ये ग्रहण द्वारा स्वतः जेनरेट कर रहे हैं:

First Set (hashCode is 80): 
    TestClass [s1=1] 
     HashCode: 80 
     Element is equal: true 
Set after changing value (hashCode is 3003475): 
    TestClass [s1=asdf] 
     HashCode: 3003475 
     Element is equal: true 
Set after trying to remove value (hashCode is 3003475): 
    TestClass [s1=asdf] 
     HashCode: 3003475 
     Element is equal: true 
Set after changing value back (hashCode is 80): 
    TestClass [s1=1] 
     HashCode: 80 
     Element is equal: true 
Set removing value (hashCode is 80): 

जब hashCode बदल गया है, मैं HashSet से मान निकालें नहीं कर सकते हैं:

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + ((s1 == null) ? 0 : s1.hashCode()); 
    return result; 
} 

@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    TestClass other = (TestClass) obj; 
    if (s1 == null) { 
     if (other.s1 != null) 
      return false; 
    } else if (!s1.equals(other.s1)) 
     return false; 
    return true; 
} 

परिणाम निम्न है। linked question में, मैं क्यों समझता हूं ऐसा क्यों है, लेकिन मुझे नहीं पता कि एक बदले गए मूल्य को कैसे हटाया जाए। क्या ऐसा करने की कोई संभावना है?

+0

क्या आप हैशकोड पोस्ट कर सकते हैं और विधियों को बराबर कर सकते हैं? – mabbas

+0

@mabbas संपादित। – looper

उत्तर

8

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

+0

दिलचस्प - क्या हैश सेट का परिणाम वास्तव में हैश मानचित्र के चारों ओर एक रैपर है? – robjohncox

+0

@robjohncox ऐसा इसलिए है क्योंकि जिस तरह से हैशिंग काम करता है। हैशकोड वस्तुओं को स्टोर और पुनर्प्राप्त करने के लिए उपयोग किया जाता है। मान लें कि आप एक मुख्य वस्तु बनाते हैं और जब आप इसे हैशैप में डालते हैं, तो हैशकोड विधि को हैश को कैल्यूलेट करने के लिए बुलाया जाएगा और बाल्टी को स्टोर करने के लिए ढूंढें। जब आप इसे पुनर्प्राप्त करने का प्रयास करते हैं, तो हैश/बाल्टी प्राप्त करने के लिए हैशकोड को फिर से बुलाया जाता है जहां कुंजी संग्रहित है। यदि आप सेट/मैप में संग्रहीत करने के बाद कुंजी ऑब्जेक्ट बदलते हैं तो हैशकोड विधि उस कुंजी ऑब्जेक्ट के लिए एक अलग हैश वापस कर देगी, जो कि कुंजी को स्टोर करने के लिए उपयोग नहीं की गई थी। –

+1

हैशसेट (हैशपैप) में संदर्भ खोजने के लिए बहुत अच्छा है, न केवल हैशकोड का उपयोग किया जाता है बल्कि बराबर भी –

1

जब आप हैशसेट में testElement जोड़ते हैं, तो यह testElement के लिए हैश कोड के आधार पर एक बाल्टी का चयन करता है। जब आप HashSet से पूछते हैं कि इसमें TestElement है, तो यह उस ऑब्जेक्ट के हैश कोड की गणना करता है जिसे वह ढूंढ रहा है और उस बाल्टी में केवल खोज करता है।

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

Testclass के लिए एक सही कार्यान्वयन s1 फ़ील्ड अंतिम के रूप में होगा।

2

जैसा कि आप विवरण से जुड़े प्रश्न के रूप में, और जैसा कि अन्य ने बताया है, आप म्यूटेबल कुंजी मुद्दे का सामना कर रहे हैं। मैं Javadoc से requote होगी:

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

जैसा कि आपने बताया, आपको वह मिलता है। सवाल यह है कि, आप वास्तव में वस्तु को कैसे हटाते हैं? आप Set.remove() का उपयोग नहीं कर सकते, क्योंकि हैश तालिका में आपकी ऑब्जेक्ट खो गई है। हालांकि, आप इसे करने के लिए Iterator का उपयोग कर सकते हैं।निम्नलिखित की तरह कुछ:

TestClass toRemove = <the same instance, but mutated>; 
for (Iterator<TestClass> iter = set.iterator(); iter.hasNext();) { 
    TestClass item = iter.next(); 
    if (toRemove.equals(item)) { 
    iter.remove(); 
    } 
} 

यह दृष्टिकोण तथ्य मानक equals() तरीकों, जैसे आप उपयोग कर रहे हैं, एक उदाहरण की जांच है पर निर्भर करता है, और कहा कि जांच सही वापस आ जाएगी।

कृपया ध्यान रखें कि यह समस्या सही इस समस्या को हल करने के लिए नहीं है। सही तरीका है कि या तो एक अपरिवर्तनीय कुंजी या "बहुत अच्छी देखभाल करें" का उपयोग करें, लेकिन यह HashSet से उत्परिवर्तित ऑब्जेक्ट को निकालने का एक तरीका है।

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