2010-11-07 9 views
15

मैं ऐसा आमतौर पर नहीं कोड पैदा करता है, लेकिन हाल ही में मैं एक विकल्प नहीं होने शुरू कर दिया। मुझे हैशसेट का सही तरीके से उपयोग करने के बारे में कुछ बड़ी गलतफहमी हो सकती है। तो यह संभव हो सकता है कि मैंने जो कुछ किया वह सिर्फ सादा गलत है। हालांकि मैं किसी भी मदद के लिए आभारी हूं, आप पेशकश कर सकते हैं। तो वास्तविक समस्या:जावा Hashset.contains() जावा में रहस्यमय परिणाम

एक छोटे से कार्यक्रम में मैं लिख रहा था, मैं बहुत ही समान वस्तुएं उत्पन्न कर रहा था, जो, जब बनाया गया, एक बहुत ही विशिष्ट आईडी (string या मेरे अंतिम पुनरावृत्ति में long) होगा। चूंकि प्रत्येक वस्तु नई वस्तुओं को जन्म देती है, इसलिए मैं उन सभी को फ़िल्टर करना चाहता हूं जिन्हें मैंने पहले ही बनाया है। इसलिए मैंने प्रत्येक नई ऑब्जेक्ट की आईडी को मेरे हैश (सेट) में फेंकना शुरू कर दिया और HashSet.contains() के साथ परीक्षण किया, यदि कोई ऑब्जेक्ट पहले बनाया गया था। यहाँ पूरा कोड है:

// hashtest.java 
import java.util.HashSet; 

class L { 
    public long l; 
    public L(long l) { 
     this.l = l; 
    } 
    public int hashCode() { 
     return (int)this.l; 
    } 
    public boolean equals(L other) { 
     return (int)this.l == (int)other.l; 
    } 
} 

class hashtest { 
    public static void main(String args[]) { 
     HashSet<L> hash = new HashSet<L>(); 
     L a = new L(2); 
     L b = new L(2); 
     hash.add(a); 
     System.out.println(hash.contains(a)); 
     System.out.println(hash.contains(b)); 
     System.out.println(a.equals(b)); 
     System.out.println(a.hashCode() == b.hashCode()); 
    } 
} 

उत्पादन निम्नलिखित का उत्पादन:

true 
false 
true 
true  

तो जाहिरा तौर पर, containsL द्वारा प्रदान की equals समारोह का उपयोग नहीं करता, या मैं अवधारणा के कुछ प्रमुख गलतफहमी है ...

मैंने इसे ओपनजेडके (वर्तमान संस्करण उबंटू में शामिल किया गया) और ओरेकल से आधिकारिक वर्तमान जावा को Win7

पर परीक्षण किया

पूर्णता आधिकारिक जावा-api प्रलेखन के लिए HashSet.contains() के लिए:

public boolean contains(Object o)

रिटर्न true इस सेट निर्दिष्ट तत्व है या नहीं। अधिक औपचारिक रूप से, true लौटाता है यदि केवल और यदि यह सेट में कोई तत्व e है, तो (o==null ? e==null : o.equals(e))

http://download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)

कोई भी विचार या सुझाव?

उत्तर

28

आपका equals विधि एक Object लेने के लिए की जरूरत है।
क्योंकि आप इसे घोषित के रूप में एक L ले, यह बजाय विधि अधिभावी का अतिरिक्त अधिभार हो जाता है।
इसलिए, जब hashSet वर्ग equals कहता है, यह आधार Object.equals विधि को हल करता है। जब आप equals पर कॉल करते हैं, तो आप अपने अधिभार को कॉल करते हैं क्योंकि a और bObject के बजाय L घोषित किए गए हैं।

को रोकने के लिए भविष्य में इस समस्या है, तो आप @Override जोड़ना चाहिए जब भी आप एक विधि को ओवरराइड।
इस तरह, संकलक आपको चेतावनी देगा कि यह वास्तव में ओवरराइड नहीं है।

+1

स्पष्टीकरण के लिए - यदि आपके पास "बराबर" के अपने संस्करण का हस्ताक्षर नहीं है, तो आप वास्तव में बराबर ओवरलोडिंग नहीं कर रहे हैं, और हैशसेट (और बाकी सब कुछ) मूल बराबर विधि का उपयोग करेगा जब तक कि यह सीधे नहीं जानता कक्षा एल के बारे में (जैसे आपका टेस्ट कोड करता है)। इसका परीक्षण करने के लिए, जोड़ें: System.out.println (a.equals ((ऑब्जेक्ट) बी)); आपके परीक्षण कार्यक्रम में - यह झूठी वापसी करनी चाहिए। –

3

आप वास्तव में Object.equals अधिभावी नहीं कर रहे हैं; इसके बजाय, आप एक ही नाम के साथ एक नई विधि को परिभाषित कर रहे हैं लेकिन विभिन्न पैरामीटर। ध्यान दें कि Object.equalsObject तर्क लेता है, जबकि आपकी बराबर विधि L तर्क लेती है।यदि आप Object लेने के लिए अपनी बराबर विधि को फिर से लिखते हैं और रनटाइम पर L पर आवश्यक टाइप-चेकिंग/कास्टिंग करते हैं, तो आपका कोड काम करता है जैसा आप उम्मीद करते हैं।

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

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

@Override 
public boolean equals(Object other) { 
    return other != null && other instanceof L && this.l == ((L)other).l; 
} 
+0

हा हा, मैंने वास्तव में किया था। मेरा मानसिक संकलक आज काम नहीं कर रहा है। :) – bcat

3

आप एक सेट वह आंतरिक रूप से equals और hashCode तरीकों कॉल करने के लिए वस्तुओं जोड़ रहे हैं। आपको इन दो तरीकों को ओवरराइड करना होगा। उदाहरण के लिए मैंने 0 बी, id, designation के साथ एक बीन क्लास लिया है, फिर बनाया और employee ऑब्जेक्ट जोड़ा।

HashSet<Employee> set = new HashSet<Employee>(); 
Employee employee = new Employee(); 
employee.setId(1); 
employee.setName("jagadeesh"); 
employee.setDesignation("programmer"); 
set.add(employee); 
Employee employee2 = new Employee(); 
employee2.setId(1); 
employee2.setName("jagadeesh"); 
employee2.setDesignation("programmer"); 
set.add(employee2); 

set.add() कॉल आंतरिक रूप से equals और hashCode तरीकों। तो आपको अपने बीन वर्ग में इन दो विधियों को ओवरराइड करना होगा।

@Override 
public int hashCode(){ 
    StringBuffer buffer = new StringBuffer(); 
    buffer.append(this.name); 
    buffer.append(this.id); 
    buffer.append(this.designation); 
    return buffer.toString().hashCode(); 
} 
@Override 
public boolean equals(Object object){ 
    if (object == null) return false; 
    if (object == this) return true; 
    if (this.getClass() != object.getClass())return false; 
    Employee employee = (Employee)object; 
    if(this.hashCode()== employee.hashCode())return true; 
    return false; 
} 

यहाँ हम equals() और hashCode() अधिभावी कर रहे हैं। जब आप HashSet विधि पर कोई ऑब्जेक्ट जोड़ते हैं तो यह आंतरिक रूप से सभी ऑब्जेक्ट्स को पुन: सक्रिय करता है और equals विधि को कॉल करता है। इसलिए हम hashCode से अधिक है, यह hashCode के साथ अपने मौजूदा hashCode के साथ तुलना करता है और दोनों बराबर होने पर सत्य लौटाता है, अन्यथा यह झूठा होता है।

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