2011-06-05 5 views
7

मैं एक HashMapकस्टम ऑब्जेक्ट्स हैश मैप के लिए समतुल्य कुंजी क्यों नहीं हैं?

public class ActorId { 
    private final int playerId; 
    private final int id; 

    ActorId(int playerId, int id) { 
     this.playerId = playerId; 
     this.id = id; 
    } 

    public boolean equals(ActorId other) { 
     return this.id == other.id && this.playerId == other.playerId; 
    } 

    public int hashCode() { 
     int hash = 1; 
     hash = hash * 31 + playerId; 
     hash = hash * 31 + id; 
     return hash; 
    } 

    public String toString() { 
     return "#" + playerId + "." + id; 
    } 

    public int getPlayerId() { 
     return playerId; 
    } 
} 

यहाँ के लिए एक प्रमुख के रूप में अपने ही वर्ग का उपयोग कर समस्या हो रही है एक असफल JUnit परीक्षण

import static org.junit.Assert.*; 
import java.util.Map; 
import org.junit.Test; 

public class ActorIdTest { 
    @Test 
    public final void testAsMapKey() { 
     ActorId a = new ActorId(123, 345); 
     ActorId b = new ActorId(123, 345); 

     assertTrue(a.equals(b)); 
     assertEquals(a.hashCode(), b.hashCode()); 

     // Works with strings as keys 
     Map<String, String> map1 = new java.util.HashMap<String, String>(); 

     map1.put(a.toString(), "test"); 
     assertEquals("test", map1.get(a.toString())); 
     assertEquals("test", map1.get(b.toString())); 
     assertEquals(1, map1.size()); 

     // But not with ActorIds 
     Map<ActorId, String> map2 = new java.util.HashMap<ActorId, String>(); 

     map2.put(a, "test"); 
     assertEquals("test", map2.get(a)); 
     assertEquals("test", map2.get(b)); // FAILS here 
     assertEquals(1, map2.size()); 

     map2.put(b, "test2"); 
     assertEquals(1, map2.size()); 
     assertEquals("test2", map2.get(a)); 
     assertEquals("test2", map2.get(b)); 
    } 
} 
+0

आप कहते हैं कि यह '... map2.get (बी)' में विफल रहता है - आपके पास आपके मानचित्र में ऐसी कोई कुंजी नहीं है। आपने मानचित्र में केवल एक ऑब्जेक्ट जोड़ा है, 'ए' उदाहरण। –

+0

@ Björn हां, दो अभिनेता आईडी ऑब्जेक्ट बराबर हैं और एक ही हैशकोड है, इसलिए उन्हें मानचित्र से वही मान वापस करना चाहिए। – dlundquist

+0

हे, क्षमा करें! बस बिस्तर से बाहर निकल गए, पूरे कोड ब्लॉक को पढ़ना चाहिए था। –

उत्तर

9

आप

public boolean equals(ActorId other) { 
    .... 
} 

को बदलने की जरूरत है है
public boolean equals(Object other) { 
    .... 
} 

दिन की युक्ति: हमेशा @Override एनोटेशन का उपयोग करें।

आप @Override एनोटेशन का इस्तेमाल किया था, तो संकलक त्रुटि पकड़ लिया और कहा है |:

विधि के बराबर होती है प्रकार ActorId की (ActorId) ओवरराइड या लागू करना चाहिए एक महाप्रकार विधि

+1

@MGwynne - आपकी टिप्पणी भ्रामक है। 1) हैश मैप एपीआई ** ** निर्दिष्ट करता है कि 'बराबर (ऑब्जेक्ट)' का उपयोग किया जाता है। 2) बस 'प्राप्त' के हस्ताक्षर को बदलना व्यवहार को नहीं बदलेगा। 'वी प्राप्त (के)' विधि को कार्यान्वित करना असंभव होगा, जिसने वास्तव में 'कक्षा ' ऑब्जेक्ट में गुजरने के बिना 'बूलियन टी.क्वल्स (के)' विधि का उपयोग किया और 'बराबर' विधि को खोजने और कॉल करने के लिए प्रतिबिंब का उपयोग करना । –

+0

@ स्टीफन सी - क्या आप मुझे इंगित कर सकते हैं कि हैश मैप एपीआई निर्दिष्ट करता है कि यह प्रकार हस्ताक्षर के अलावा बराबर (ऑब्जेक्ट) का उपयोग करता है? जहां तक ​​मुझे पता है, प्राप्त विधि बस वस्तुओं पर समान विधि को कॉल करती है। मुझे अत्यधिक संदेह है कि यह विशेष रूप से उन्हें ऑब्जेक्ट करने के लिए रोकता है, क्योंकि यह पहले से ही हस्ताक्षर द्वारा दिया गया है। एकमात्र बिंदु यह है कि प्राप्त विधि * हस्ताक्षर * में निर्दिष्ट करती है * यह एक वस्तु लेती है और इसलिए ओवरलोडेड बराबर विधि छिपी हुई है। यदि हस्ताक्षर को 'वी गेट (के कुंजी)' में बदल दिया गया था, तो यह @dundquist अपेक्षित के रूप में काम करेगा। क्या मैं कुछ गलत समझ रहा हूँ? – MGwynne

+0

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

3

आपका कोड सही है, लेकिन आपको Object से प्राप्त equals विधि को ओवरराइड करने की भी आवश्यकता है।

अपने ActorId वर्ग को यह करें:

@Override 
public boolean equals(Object other) { 
    if(other == null || other.getClass() != getClass()) 
     return false; 
    return equals((ActorId)other); 
} 
1

आप निश्चित रूप से ओवरराइड चाहिए विधि के बराबर होती है (वस्तु), और एक मानचित्र (HashMap) के कुछ कार्यान्वयन के लिए यह भी आवश्यक है कि आप विधि hashCode overrdide ()।

मुझे एक ही समस्या थी, और कस्टम हैशकोड कार्यान्वयन के बिना कक्षा "अभिनेता आईडी" के बराबर विधि कभी नहीं बुलाया गया था।

0

डिफ़ॉल्ट रूप से जावा बूलियन invokes equals(Object obj); तो, आप के लिए लॉग इन सही है, लेकिन यदि आप बराबर ओवरराइड करने के लिए() चाहते हैं एक पैरामीटर और के रूप में वस्तु का उपयोग करें और instanceOf या getClass() द्वारा वर्ग की जाँच करें और एक वर्ग के कास्टिंग है।

if (obj instanceOf ActorId) { 
    ActorId other = (ActorId)obj; 
    ... compare fields 
} 
संबंधित मुद्दे