Java में HashMap और Map ऑब्जेक्ट्स के बीच अंतर क्या है?


349

मेरे द्वारा बनाए गए निम्नलिखित मानचित्रों के बीच अंतर क्या है (एक अन्य प्रश्न में, लोगों ने उन्हें उचित रूप से उपयोग करते हुए उत्तर दिया और मुझे आश्चर्य है कि क्या / वे कैसे भिन्न हैं):

HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();

मान लीजिए कि आपने हाशप और मैरी का उपयोग करके मानचित्र का उपयोग किया है। क्या यह संकलन करेगा?
गिल्बर्टएस

जवाबों:


446

वस्तुओं के बीच कोई अंतर नहीं है; आप HashMap<String, Object>दोनों मामलों में एक है। आपके पास ऑब्जेक्ट के लिए इंटरफ़ेस में अंतर है । पहले मामले में, इंटरफ़ेस है HashMap<String, Object>, जबकि दूसरे में Map<String, Object>। लेकिन अंतर्निहित वस्तु समान है।

उपयोग करने का लाभ यह Map<String, Object>है कि आप किसी भी कोड के साथ अपने अनुबंध को तोड़ने के बिना अंतर्निहित वस्तु को एक अलग तरह का नक्शा बदल सकते हैं। यदि आप इसे घोषित करते हैं HashMap<String, Object>, तो आपको अपना अनुबंध बदलना होगा यदि आप अंतर्निहित कार्यान्वयन को बदलना चाहते हैं।


उदाहरण: मान लीजिए कि मैं यह वर्ग लिखता हूँ:

class Foo {
    private HashMap<String, Object> things;
    private HashMap<String, Object> moreThings;

    protected HashMap<String, Object> getThings() {
        return this.things;
    }

    protected HashMap<String, Object> getMoreThings() {
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

कक्षा में स्ट्रिंग के आंतरिक मानचित्र के कुछ जोड़े हैं-> ऑब्जेक्ट जो इसे उप-वर्ग के साथ साझा करता है (एक्सेसर विधियों के माध्यम से)। मान लें कि मैं इसे HashMapएस के साथ शुरू करने के लिए लिखता हूं क्योंकि मुझे लगता है कि कक्षा लिखते समय उपयोग करने के लिए उपयुक्त संरचना है।

बाद में, मैरी ने इसे उप-वर्गित करते हुए कोड लिखा। उसके पास कुछ ऐसा है जो उसे दोनों के साथ करने की आवश्यकता है , thingsऔर moreThingsस्वाभाविक रूप से वह उसे एक सामान्य विधि में रखती है, और वह उसी प्रकार का उपयोग करती है जिसका मैंने उपयोग किया getThings/ getMoreThingsजब उसकी विधि को परिभाषित किया:

class SpecialFoo extends Foo {
    private void doSomething(HashMap<String, Object> t) {
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }

    // ...more...
}

बाद में, मैंने तय करते हैं कि वास्तव में, यह बेहतर होगा यदि मैं का उपयोग TreeMapकरने के बजाय HashMapमें Foo। मैं अद्यतन Foo, बदल रहा है HashMapकरने के लिए TreeMap। अब, SpecialFooकोई संकलन नहीं करता है, क्योंकि मैंने अनुबंध को तोड़ दिया है: Fooयह कहने के लिए उपयोग किया गया है कि यह प्रदान किया गया है HashMap, लेकिन अब यह TreeMapsइसके बजाय प्रदान कर रहा है। तो हमें SpecialFooअब ठीक करना होगा (और इस तरह की चीज़ कोडबेस के माध्यम से रिपल हो सकती है)।

जब तक कि मेरे पास साझा करने के लिए वास्तव में एक अच्छा कारण नहीं था कि मेरा कार्यान्वयन एक HashMap(और ऐसा होता है) का उपयोग कर रहा था, मुझे जो करना चाहिए था वह घोषित किया गया था getThingsऔर getMoreThingsजैसा Map<String, Object>कि किसी भी विशिष्ट से अधिक विशिष्ट होने के बिना लौट रहा था। वास्तव में, कुछ और करने के लिए एक अच्छा कारण को छोड़कर, यहां तक ​​कि Fooशायद मुझे घोषित करना चाहिए thingsऔर moreThingsजैसा कि Map, नहीं HashMap/ TreeMap:

class Foo {
    private Map<String, Object> things;             // <== Changed
    private Map<String, Object> moreThings;         // <== Changed

    protected Map<String, Object> getThings() {     // <== Changed
        return this.things;
    }

    protected Map<String, Object> getMoreThings() { // <== Changed
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

ध्यान दें कि मैं अब Map<String, Object>हर जगह का उपयोग कैसे कर सकता हूं, केवल विशिष्ट होने के नाते जब मैं वास्तविक वस्तुओं का निर्माण करता हूं।

अगर मैंने ऐसा किया होता, तो मैरी ने ऐसा किया होता:

class SpecialFoo extends Foo {
    private void doSomething(Map<String, Object> t) { // <== Changed
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }
}

... और बदलने Fooसे SpecialFooस्टॉप संकलन नहीं हो जाता ।

इंटरफेस (और बेस क्लास) हमें केवल आवश्यकतानुसार परिवर्तन करने के लिए कवर के तहत हमारे लचीलेपन को बनाए रखते हुए, जितना आवश्यक हो उतना ही प्रकट करते हैं । सामान्य तौर पर, हम चाहते हैं कि हमारे संदर्भ यथासंभव बुनियादी हों। अगर हमें यह जानने की आवश्यकता नहीं है कि यह एक है HashMap, तो इसे कॉल करें a Map

यह एक अंधा नियम नहीं है, लेकिन सामान्य तौर पर, सबसे सामान्य इंटरफ़ेस के लिए कोडिंग कुछ अधिक विशिष्ट कोडिंग की तुलना में कम भंगुर होने वाली है। अगर मुझे याद है कि, मैंने Fooअसफलता के लिए मैरी को सेट नहीं किया है SpecialFoo। अगर मैरी को वह याद था, तो फिर भी मैंने गड़बड़ की Foo, तो उसने अपनी निजी पद्धति को Mapइसके बजाय घोषित कर दिया होगा HashMapऔर मेरे बदलते Fooअनुबंध ने उसके कोड को प्रभावित नहीं किया होगा।

कभी-कभी आप ऐसा नहीं कर सकते, कभी-कभी आपको विशिष्ट होना पड़ता है। लेकिन जब तक आपके पास कोई कारण न हो, कम से कम विशिष्ट इंटरफ़ेस की ओर।


56

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

यदि आप सूर्य के इंटरफ़ेस ट्यूटोरियल को पढ़ते हैं तो यह संभवतः अधिक समझ में आएगा


मैं मानता हूं: पहले = हैशपेयर <स्ट्रींग, ऑब्जेक्ट> मैप = न्यू हैशमैप <स्ट्रिंग, ऑब्जेक्ट> ();
वनवर्ल्ड

यह समान है कि एक सूची को ArrayList के रूप में कितनी बार लागू किया जाता है
जेरार्ड

26

यहां छवि विवरण दर्ज करें

मानचित्र में निम्नलिखित कार्यान्वयन हैं:

  1. हैश मैप Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. ट्री मैप Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

मान लीजिए आपने एक विधि बनाई है (यह सिर्फ छद्म कोड है)।

public void HashMap getMap(){
   return map;
}

मान लीजिए कि आपकी परियोजना की आवश्यकताएं बदल गई हैं:

  1. विधि को नक्शे की सामग्री वापस करनी चाहिए - वापस करने की आवश्यकता है HashMap
  2. प्रविष्टि क्रम में विधि को मानचित्र की कुंजी वापस करनी चाहिए - वापसी प्रकार HashMapको बदलने की आवश्यकता है LinkedHashMap
  3. विधि को क्रमबद्ध क्रम में मानचित्र की को वापस करना चाहिए - वापसी प्रकार LinkedHashMapको बदलने की आवश्यकता है TreeMap

यदि आपका तरीका Mapइंटरफ़ेस को लागू करने वाली किसी चीज़ के बजाय विशिष्ट कक्षाएं देता है, तो आपको getMap()हर बार रिटर्न प्रकार को बदलना होगा ।

लेकिन अगर आप जावा की बहुरूपता सुविधा का उपयोग करते हैं, और विशिष्ट कक्षाओं को वापस करने के बजाय, इंटरफ़ेस का उपयोग करते हैं Map, तो यह कोड पुन: प्रयोज्य में सुधार करता है और आवश्यकता परिवर्तनों के प्रभाव को कम करता है।


17

मैं इसे स्वीकार किए गए उत्तर पर एक टिप्पणी के रूप में करने जा रहा था, लेकिन यह बहुत मजेदार हो गया (मुझे लाइन ब्रेक नहीं होने से नफरत है)

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

बिल्कुल - और आप हमेशा सबसे सामान्य इंटरफ़ेस का उपयोग करना चाहते हैं जो आप संभवतः कर सकते हैं। ArrayList बनाम LinkedList पर विचार करें। आप उन्हें कैसे उपयोग करते हैं, इसमें बड़ा अंतर है, लेकिन यदि आप "सूची" का उपयोग करते हैं, तो आप उनके बीच आसानी से स्विच कर सकते हैं।

वास्तव में, आप इनिशियलाइज़र के दाहिने हाथ को अधिक गतिशील स्टेटमेंट से बदल सकते हैं। इस जैसे किसी और के बारे में क्या राय है:

List collection;
if(keepSorted)
    collection=new LinkedList();
else
    collection=new ArrayList();

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

यह एक बहुत बड़ा खिंचाव है क्योंकि संग्रह सबसे अच्छा उदाहरण नहीं हैं, लेकिन OO डिज़ाइन में सबसे महत्वपूर्ण अवधारणाओं में से एक सटीक कोड के साथ विभिन्न वस्तुओं तक पहुंचने के लिए इंटरफ़ेस मुखौटा का उपयोग कर रहा है।

टिप्पणी का जवाब संपादित करें:

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

अक्सर आप जो करते हैं, वह एक ऑब्जेक्ट बनाता है और इसे विशिष्ट प्रकार (HashMap) का उपयोग करके भरता है, किसी प्रकार के "बनाएँ" या "इनिशियलाइज़" विधि में, लेकिन वह विधि एक "मैप" लौटाएगी, जिसकी आवश्यकता नहीं है किसी भी अधिक HashMap के रूप में हेरफेर।

यदि आपको कभी रास्ते से कास्ट करना है, तो आप शायद गलत इंटरफ़ेस का उपयोग कर रहे हैं या आपका कोड पर्याप्त रूप से संरचित नहीं है। ध्यान दें कि आपके कोड के एक हिस्से को "हैशपॉप" के रूप में माना जाता है जबकि दूसरा इसे "मैप" के रूप में मानता है, लेकिन यह "डाउन" होना चाहिए। ताकि आप कभी कास्टिंग न करें।

इंटरफेस द्वारा इंगित भूमिकाओं के अर्ध-स्वच्छ पहलू को भी देखें। एक LinkedList एक अच्छा स्टैक या कतार बनाता है, एक ArrayList एक अच्छा स्टैक बनाता है, लेकिन एक भयावह कतार (फिर से, पूरी सूची को हटाने का कारण होगा) इसलिए LinkedList क्यू इंटरफ़ेस को लागू करता है, ArrayList नहीं करता है।


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

मुझे लगता है कि मैं क्या देख रहा हूं या नहीं जब मैं कहता हूं कि क्या मैं मैप <string, string> m = new HashMap <string, string> () मेरा मैप m हैशपॉप के लिए विशिष्ट तरीकों का उपयोग कर सकता है, या नहीं। मैं यह नहीं कर सकता सोच रहा हूँ?
टोनी स्टार्क

आह, रुको, नहीं, ऊपर से मेरे मैप मी के पास हैशपॉप से ​​विधियां होनी चाहिए।
टोनी स्टार्क

इसलिए मूल रूप से 'इंटरफ़ेस अर्थ' में मैप का उपयोग करने का एकमात्र अर्थ यह है कि अगर मेरे पास कोई ऐसा तरीका है जिसके लिए मानचित्र की आवश्यकता होती है, तो मैं किसी भी प्रकार के नक्शे की गारंटी देता हूं इस पद्धति में काम करेगा। लेकिन अगर मैंने एक हैशमैप का उपयोग किया है, तो मैं कह रहा हूं कि विधि केवल हैशमैप के साथ काम करती है। या, एक और तरीका है, मेरी विधि केवल मानचित्र वर्ग में परिभाषित विधियों का उपयोग करती है लेकिन अन्य वर्गों द्वारा विरासत में मिली है जो मानचित्र का विस्तार करते हैं।
टोनी स्टार्क

आपके द्वारा उल्लिखित पर्क के अलावा, जहां सूची का उपयोग करने का मतलब है कि मुझे यह तय करने की आवश्यकता नहीं है कि मैं रनटाइम तक किस प्रकार की सूची चाहता हूं, जबकि यदि इंटरफ़ेस चीज मौजूद नहीं थी, तो मुझे संकलन और चलाने से पहले एक चुनना होगा
टोनी स्टार्क

12

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


8

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

दूसरा दृष्टिकोण आम तौर पर पसंद किया जाता है क्योंकि आप आमतौर पर Mapएपीआई परिभाषा का उपयोग करके या उसके माध्यम से विधियों के लिए विशिष्ट मानचित्र कार्यान्वयन को उजागर नहीं करना चाहते हैं ।


8

मानचित्र स्थैतिक प्रकार का मानचित्र है, जबकि हैशपैप गतिशील प्रकार का मानचित्र है। इसका मतलब यह है कि कंपाइलर आपके मैप ऑब्जेक्ट को एक प्रकार का मैप मान लेगा, भले ही रनटाइम के दौरान, यह इसके किसी भी उपप्रकार को इंगित कर सकता है।

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

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


4

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

Mapसंरचना अनुबंध है, जबकि HashMapएक कार्यान्वयन विभिन्न वास्तविक समस्याओं से निपटने के लिए अपने स्वयं के तरीके प्रदान करता है: सूचकांक की गणना कैसे करें, क्षमता क्या है और इसे कैसे बढ़ाया जाए, कैसे सम्मिलित किया जाए, कैसे सूचकांक को अद्वितीय रखा जाए, आदि।

स्रोत कोड पर नजर डालते हैं:

में Mapहम की विधि है containsKey(Object key):

boolean containsKey(Object key);

JavaDoc:

बूलियन java.util.Map.containsValue (ऑब्जेक्ट मान)

यदि यह मानचित्र निर्दिष्ट मान पर एक या अधिक कुंजियों को मैप करता है, तो यह सही है। अधिक औपचारिक रूप से, सच है अगर और केवल अगर इस नक्शे में कम से कम एक मानचित्रण शामिल है vजैसे कि मूल्य (value==null ? v==null : value.equals(v))। इस ऑपरेशन को मैप इंटरफ़ेस के अधिकांश कार्यान्वयन के लिए मैप आकार में समय रेखीय की आवश्यकता होगी।

पैरामीटर: मूल्य

इस मानचित्र में किसकी उपस्थिति दांव पर लगी है

यह दिखाता है: सच

यदि यह मानचित्र निर्दिष्ट की गई एक या अधिक कुंजियों को मैप करता है

valueThrows:

ClassCastException - यदि मान इस मानचित्र के लिए अनुपयुक्त प्रकार का है (वैकल्पिक)

NullPointerException - यदि निर्दिष्ट मान शून्य है और यह मानचित्र अशक्त मानों (वैकल्पिक) की अनुमति नहीं देता है

इसे लागू करने के लिए इसके कार्यान्वयन की आवश्यकता होती है, लेकिन "कैसे" इसकी स्वतंत्रता पर है, केवल यह सुनिश्चित करने के लिए कि यह सही है।

इन HashMap:

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

यह पता चलता है कि HashMapयदि इस मानचित्र में कुंजी है तो यह परीक्षण करने के लिए हैशकोड का उपयोग करता है। तो यह हैश एल्गोरिथ्म का लाभ है।


3

आप वही नक्शे बनाते हैं।

लेकिन आप अंतर तब भर सकते हैं जब आप इसका उपयोग करेंगे। पहले मामले में आप विशेष HashMap विधियों का उपयोग करने में सक्षम होंगे (लेकिन मुझे कोई भी उपयोगी नहीं याद है), और आप इसे HashMap पैरामीटर के रूप में पारित करने में सक्षम होंगे:

public void foo (HashMap<String, Object) { ... }

...

HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;

foo (m1);
foo ((HashMap<String, Object>)m2); 

3

मैप इंटरफ़ेस है और हैशमैप एक वर्ग है जो मैप इंटरफ़ेस को लागू करता है


1

नक्शा इंटरफ़ेस है और हैशमैप वह वर्ग है जो लागू करता है।

तो इस कार्यान्वयन में आप एक ही वस्तु बनाते हैं


0

HashMap मानचित्र का कार्यान्वयन है, इसलिए यह काफी समान है लेकिन इसमें "क्लोन ()" विधि है जैसा कि मैं संदर्भ गाइड में देखता हूं)


0
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();  

सबसे पहले Mapएक अंतरफलक यह की तरह अलग अलग कार्यान्वयन है - HashMap, TreeHashMap, LinkedHashMapआदि इंटरफेस को लागू करने वर्ग के लिए एक सुपर वर्ग की तरह काम करता है। इसलिए OOP के नियम के अनुसार कोई भी ठोस वर्ग जो लागू करता Mapहै वह Mapभी एक है । इसका मतलब है कि हम किसी भी प्रकार की कास्टिंग के बिना किसी भी HashMapप्रकार के वेरिएबल को किसी भी प्रकार के वेरिएबल में असाइन / डाल सकते हैं Map

इस मामले में हम प्रदान कर सकते हैं map1करने के लिए map2किसी भी कास्टिंग या डेटा के किसी भी खोने के बिना -

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