एक जावा हैशपॅप विभिन्न वस्तुओं को एक ही हैश कोड के साथ कैसे संभालता है?


223

मेरी समझ के अनुसार:

  1. यह दो वस्तुओं के लिए समान हैशकोड के लिए पूरी तरह से कानूनी है।
  2. यदि दो ऑब्जेक्ट समान हैं (बराबर () विधि का उपयोग करके) तो उनके पास समान हैशकोड है।
  3. यदि दो वस्तुएं समान नहीं हैं, तो उनके पास समान हैशकोड नहीं हो सकता है

क्या मैं सही हूँ?

अब अगर मैं सही हूं, तो मेरे पास निम्नलिखित प्रश्न हैं: HashMapआंतरिक रूप से ऑब्जेक्ट के हैशकोड का उपयोग करता है। तो अगर दो वस्तुओं में एक ही हैशकोड हो सकता है, तो वह किस HashMapट्रैक का उपयोग कर सकता है?

क्या कोई यह बता सकता है कि HashMapआंतरिक वस्तु के हैशकोड का उपयोग कैसे करता है?


29
रिकॉर्ड के लिए: # 1 और # 2 सही हैं, # 3 गलत है: दो ऑब्जेक्ट जो समान नहीं हैं उनमें समान हैश कोड हो सकता है।
जोकिम सॉयर

6
# 1 और # 3 विरोधाभासी भी हैं
Delfic

दरअसल, अगर # 2 का पालन नहीं किया जाता है, तो बराबर () कार्यान्वयन (या यकीनन हैशकोड ()) गलत है।
जोआचिम सॉर

जवाबों:


346

एक हैशमैप इस तरह काम करता है (यह थोड़ा सरल है, लेकिन यह बुनियादी तंत्र को दिखाता है):

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

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

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

ऊपर तंत्र को देखते हुए, आप भी देख सकते हैं कि आवश्यकताओं पर आवश्यक हैं hashCode()और equals()चाबियों का तरीके:

  • यदि दो कुंजियाँ समान हैं ( जब आप उनकी तुलना करते हैं तो equals()रिटर्न true), उनकी hashCode()विधि को उसी संख्या को वापस करना होगा। यदि कुंजियाँ इसका उल्लंघन करती हैं, तो समान कुंजियाँ अलग-अलग बाल्टियों में संग्रहीत की जा सकती हैं, और हैशमैप कुंजी-मूल्य जोड़े (क्योंकि यह एक ही बाल्टी में देखने जा रहा है) को खोजने में सक्षम नहीं होगा।

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


4
आपने लिखा "और हैशमैप कुंजी-मूल्य वाले जोड़े नहीं ढूंढ पाएंगे (क्योंकि यह एक ही बाल्टी में दिखने वाला है)।" क्या आप बता सकते हैं कि यह एक ही बाल्टी में दिखने वाली है, कह सकते हैं कि दो समान वस्तुएं t1 और t2 हैं और वे समान हैं और t1 और t2 में क्रमशः हैशकोड h1 और h2 हैं। t1.equals (t2) = true और h1! = H2 तो जब हैशमैप t1 के लिए दिखेगा, तो यह बाल्टी h1 में और t2 के लिए बाल्टी t2 में दिखेगा?
गीक

19
दो कुंजी के बराबर हैं लेकिन उनकी तो hashCode()विधि अलग हैश कोड देता है, तो equals()और hashCode()कुंजी वर्ग के तरीकों अनुबंध का उल्लंघन है और आप जब एक में उन कुंजियों का उपयोग अजीब परिणाम प्राप्त करेंगे HashMap
जेसपर

प्रत्येक बाल्टी में कई कुंजी मूल्य जोड़े हो सकते हैं, जो आंतरिक रूप से लिंक की गई सूची का उपयोग करते हैं। लेकिन मेरा भ्रम है - यहाँ बाल्टी क्या है? आंतरिक रूप से यह किस डेटा संरचना का उपयोग करता है? क्या बाल्टी के बीच कोई संबंध है?
अंकित शर्मा

1
@AnkitSharma यदि आप वास्तव में सभी विवरण जानना चाहते हैं, तो उस स्रोत कोड को देखें HashMap, जिसे आप src.zipअपनी JDK इंस्टॉलेशन डायरेक्टरी में फ़ाइल में पा सकते हैं ।
जेसपर

1
@ 1290 एक ही बाल्टी में कुंजियों के बीच एकमात्र संबंध यह है कि उनके पास समान हैश कोड है।
जेसपर

88

आपका तीसरा दावा गलत है।

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

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


6
आप 2 ^ 32 संभावित वस्तुओं पर कैसे पहुंचे?
गीक

5
मुझे देर हो रही है, लेकिन अभी भी सोच रहे लोगों के लिए: जावा में एक
हैशकोड

69

HashMap संरचना आरेख

HashMapEntryवस्तुओं की एक सरणी है ।

HashMapवस्तुओं की एक सरणी के रूप में विचार करें ।

इस पर एक नज़र डालें Object:

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
 
}

प्रत्येक Entryऑब्जेक्ट एक कुंजी-मूल्य जोड़ी का प्रतिनिधित्व करता है। यदि कोई बाल्टी एक से अधिक है, तो फ़ील्ड nextकिसी अन्य Entryऑब्जेक्ट को संदर्भित करता है Entry

कभी-कभी ऐसा हो सकता है कि 2 अलग-अलग वस्तुओं के लिए हैश कोड समान हों। इस मामले में, दो वस्तुओं को एक बाल्टी में सहेजा जाएगा और एक लिंक की गई सूची के रूप में प्रस्तुत किया जाएगा। प्रवेश बिंदु अधिक हाल ही में जोड़ी गई वस्तु है। यह ऑब्जेक्ट nextफ़ील्ड के साथ किसी अन्य ऑब्जेक्ट को संदर्भित करता है और इसी तरह। अंतिम प्रविष्टि को संदर्भित करता है null

जब आप HashMapडिफ़ॉल्ट कंस्ट्रक्टर के साथ बनाते हैं

HashMap hashMap = new HashMap();

सरणी को आकार 16 और डिफ़ॉल्ट 0.75 लोड संतुलन के साथ बनाया गया है।

एक नया कुंजी-मूल्य युग्म जोड़ना

  1. कुंजी के लिए हैशकोड की गणना करें
  2. स्थिति की गणना करें hash % (arrayLength-1)जहां तत्व रखा जाना चाहिए (बाल्टी संख्या)
  3. यदि आप एक कुंजी के साथ एक मूल्य जोड़ने का प्रयास करते हैं जो पहले से ही सहेजा गया है HashMap, तो मान अधिलेखित हो जाता है।
  4. अन्यथा बाल्टी में तत्व मिलाया जाता है।

यदि बाल्टी में पहले से ही कम से कम एक तत्व है, तो एक नया जोड़ा जाता है और बाल्टी की पहली स्थिति में रखा जाता है। इसका nextक्षेत्र पुराने तत्व को संदर्भित करता है।

विलोपन

  1. दिए गए कुंजी के लिए हैशकोड की गणना करें
  2. बकेट नंबर की गणना करें hash % (arrayLength-1)
  3. बाल्टी में पहली प्रविष्टि वस्तु का संदर्भ प्राप्त करें और दिए गए बाल्टी में सभी प्रविष्टियों के बराबर पद्धति के अनुसार। आखिरकार हम सही पाएंगे Entry। यदि वांछित तत्व नहीं मिला है, तो वापस लौटेंnull

2
यह गलत है hash % (arrayLength-1)यह होगा hash % arrayLength। लेकिन यह वास्तव में है hash & (arrayLength-1) । यह है, क्योंकि यह 2^nसरणी लंबाई के लिए दो की शक्तियों का उपयोग करता है , nकम से कम महत्वपूर्ण बिट्स ले रहा है ।
वेस्टन

मुझे लगता है कि यह लिंक्डलिस्ट / ट्री की एक सरणी के बजाय इकाई वस्तुओं की एक सरणी नहीं है। और प्रत्येक पेड़ में आंतरिक रूप से एंटिटी ऑब्जेक्ट होते हैं।
मुदित भैंतवाल

@shevchyk हम कुंजी और हैश क्यों स्टोर करते हैं? उनका क्या उपयोग है? क्या हम यहां स्मृति को बर्बाद नहीं कर रहे हैं?
रोज़ट्रेलर

हैशसेट आंतरिक रूप से हैशमैप का उपयोग करता है। हैशमैप के अलावा और विलोपन नियम, हैशसेट के लिए अच्छा है?
ओवरएक्सचेंज

2
@ वेस्टन ही नहीं, हैशकोड intनिश्चित रूप से नकारात्मक हो सकता है, एक ऋणात्मक संख्या पर मोडुलो करने से आपको एक नकारात्मक संख्या मिलेगी
यूजीन

35

आप http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-ava.html पर उत्कृष्ट जानकारी प्राप्त कर सकते हैं

संक्षेप में:

हैशपॉप हैशिंग के सिद्धांत पर काम करता है

put (की, वेल्यू): HashMap , मैप और एनीट्री के रूप में कुंजी और वैल्यू ऑब्जेक्ट दोनों को स्टोर करता है। बाल्टी पाने के लिए हैशमैप हैशकोड (कुंजी) लागू करता है। यदि टक्कर होती है, तो HashMap ऑब्जेक्ट को स्टोर करने के लिए लिंक्डलिस्ट का उपयोग करता है।

get (की): HashMap बकेट लोकेशन का पता लगाने के लिए Key Object के हैशकोड का उपयोग करता है और फिर LinkedList में सही नोड की पहचान करने और Java HashMap में उस कुंजी के लिए संबद्ध मान ऑब्जेक्ट को वापस करने के लिए keys.equals () विधि को कॉल करता है।


3
मुझे जैस्पर द्वारा प्रदान किया गया उत्तर बेहतर लगा, मुझे लगा कि ब्लॉग इंटरव्यू को संभालने की ओर अधिक है, अवधारणा को समझने की तुलना में
नरेंद्र एन।

@ नरेंद्रन मैं आपसे सहमत हूँ।
अभिजीत गायकवाड़

22

यहाँ संस्करण के HashMapलिए तंत्र का एक मोटा विवरण है Java 8, (यह जावा 6 से थोड़ा अलग हो सकता है)


डेटा संरचनाएं

  • हैश टेबल
    हैश मान को hash()कुंजी के माध्यम से परिकलित किया जाता है, और यह तय करता है कि किसी दिए गए कुंजी के लिए किस हैशटेबल का उपयोग किया जाए।
  • लिंक की गई सूची (एकवचन)
    जब एक बाल्टी में तत्वों की गिनती छोटी होती है, तो एक लिंक की गई सूची का उपयोग किया जाता है।
  • लाल-काला पेड़
    जब बाल्टी में तत्वों की गिनती बड़ी होती है, तो लाल-काले रंग के पेड़ का उपयोग किया जाता है।

कक्षाएं (आंतरिक)

  • Map.Entry
    मानचित्र में एकल इकाई, कुंजी / मूल्य इकाई का प्रतिनिधित्व करते हैं।
  • HashMap.Node
    नोड की लिंक की गई सूची संस्करण।

    यह प्रतिनिधित्व कर सकता है:

    • एक हैश बाल्टी।
      क्योंकि इसके पास हैश प्रॉपर्टी है।
    • एकल लिंक की गई सूची में एक नोड, (इस प्रकार लिंकलिस्ट का प्रमुख भी)
  • HashMap.TreeNode
    नोड का वृक्ष संस्करण।

फ़ील्ड (आंतरिक)

  • Node[] table
    बाल्टी तालिका, (लिंक की गई सूचियों का प्रमुख)।
    यदि बाल्टी में तत्व नहीं हैं, तो यह शून्य है, इस प्रकार केवल एक संदर्भ का स्थान लेते हैं।
  • Set<Map.Entry> entrySet संस्थाओं का सेट।
  • int size
    संस्थाओं की संख्या।
  • float loadFactor
    संकेत दें कि आकार बदलने से पहले हैश तालिका को पूरा करने की अनुमति कैसे दी जाती है।
  • int threshold
    अगला आकार जिस पर आकार बदलना है।
    सूत्र:threshold = capacity * loadFactor

तरीके (आंतरिक)

  • int hash(key)
    हैश की गणना कुंजी से करें।
  • हैश टू बकेट मैप कैसे करें?
    निम्नलिखित तर्क का उपयोग करें:

    static int hashToBucket(int tableSize, int hash) {
        return (tableSize - 1) & hash;
    }

क्षमता के बारे में

हैश तालिका में, क्षमता का अर्थ है बाल्टी की गिनती, इससे प्राप्त किया जा सकता है table.length
इसके अलावा के माध्यम से गणना की जा सकती thresholdहै और loadFactor, इस प्रकार कोई एक वर्ग क्षेत्र के रूप में परिभाषित किया जाना चाहिए।

के माध्यम से प्रभावी क्षमता प्राप्त कर सकता है: capacity()


संचालन

  • कुंजी द्वारा इकाई का पता लगाएं।
    सबसे पहले बाल्टी को हैश वैल्यू से खोजें, फिर लूप लिंक्ड लिस्ट या सर्च सॉर्ट ट्री को खोजें।
  • कुंजी के साथ इकाई जोड़ें।
    सबसे पहले बाल्टी को हैश के मान के अनुसार खोजें।
    फिर मूल्य खोजने की कोशिश करें:
    • यदि पाया जाता है, तो मान बदलें।
    • अन्यथा, लिंक्ड सूची की शुरुआत में एक नया नोड जोड़ें, या सॉर्ट किए गए पेड़ में डालें।
  • आकार बदलें
    जब thresholdपहुंचे तो hashtable की क्षमता (दोगुना होगा table.length), तो तालिका के पुनर्निर्माण के लिए सभी तत्वों पर एक फिर से हैश प्रदर्शन करते हैं।
    यह एक महंगा ऑपरेशन हो सकता है।

प्रदर्शन

  • प्राप्त करें और
    समय जटिलता डालेंO(1) , क्योंकि:
    • इस प्रकार बकेट ऐरे इंडेक्स के माध्यम से पहुँचा जाता है O(1)
    • प्रत्येक बाल्टी में लिंक्ड सूची छोटी लंबाई की है, इस प्रकार देख सकते हैं O(1)
    • पेड़ का आकार भी सीमित है, क्योंकि तत्व की गिनती बढ़ने पर क्षमता और फिर से विस्तार होगा, इसलिए इसे इस रूप में देख सकते हैं O(1), नहीं O(log N)

क्या आप कृपया एक उदाहरण दे सकते हैं कि समय जटिलता ओ (1)
जितेंद्र

@jsroyal यह जटिलता को अधिक स्पष्ट रूप से समझा सकता है: en.wikipedia.org/wiki/Hash_table । लेकिन संक्षेप में: लक्ष्य बाल्टी ढूंढना हे (1) है, क्योंकि आप इसे किसी सरणी में सूचकांक द्वारा पाते हैं; फिर एक बाल्टी के भीतर, राशि तत्व छोटे होते हैं और पूरे हैशटेब में तत्वों की कुल संख्या के बावजूद औसतन एक स्थिर संख्या होती है, इसलिए एक बाल्टी के भीतर लक्ष्य तत्व की खोज भी ओ (1) है; इस प्रकार, O (1) + O (1) = O (1)।
एरिक वांग

14

हैशकोड यह निर्धारित करता है कि किस हैमपैप के लिए बाल्टी को जांचना है। यदि बाल्टी में एक से अधिक वस्तु है तो एक रेखीय खोज की जाती है ताकि यह पता चले कि बाल्टी में कौन सी वस्तु वांछित (के बराबर) हैequals() विधि )।

दूसरे शब्दों में, यदि आपके पास एक सही हैशकोड है, तो हैशमैप एक्सेस स्थिर है, आपको कभी भी बाल्टी के माध्यम से पुनरावृत्त नहीं होना पड़ेगा (तकनीकी रूप से आपके पास MAX_INT बाल्टी भी होगी, जावा कार्यान्वयन उसी बाल्टी में कुछ हैश कोड साझा कर सकता है। अंतरिक्ष आवश्यकताओं पर कटौती)। यदि आपके पास सबसे खराब हैशकोड (हमेशा एक ही नंबर लौटाता है) तो आपकी हैशमैप पहुंच रैखिक हो जाती है क्योंकि आपको मानचित्र में प्रत्येक आइटम (वे सभी एक ही बाल्टी में हैं) के माध्यम से खोज करना है कि आप क्या चाहते हैं।

अधिकांश समय एक अच्छी तरह से लिखा गया हैशकोड सही नहीं है, लेकिन आपको अधिक या कम निरंतर पहुंच प्रदान करने के लिए अद्वितीय है।


11

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

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


6
import java.util.HashMap;

public class Students  {
    String name;
    int age;

    Students(String name, int age ){
        this.name = name;
        this.age=age;
    }

    @Override
    public int hashCode() {
        System.out.println("__hash__");
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println("__eq__");
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Students other = (Students) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    public static void main(String[] args) {

        Students S1 = new Students("taj",22);
        Students S2 = new Students("taj",21);

        System.out.println(S1.hashCode());
        System.out.println(S2.hashCode());

        HashMap<Students,String > HM = new HashMap<Students,String > (); 
        HM.put(S1, "tajinder");
        HM.put(S2, "tajinder");
        System.out.println(HM.size());
    }
}

Output:

__ hash __

116232

__ hash __

116201

__ hash __

__ hash __

2

इसलिए यहाँ हम देखते हैं कि यदि दोनों वस्तुओं S1 और S2 में अलग-अलग सामग्री है, तो हमें पूरा यकीन है कि हमारी ओवरराइड की गई हैशकोड विधि दोनों वस्तुओं के लिए अलग-अलग हैशकोड (116232,11601) उत्पन्न करेगी। अब चूंकि अलग-अलग हैश कोड हैं, इसलिए यह EQUALS विधि को कॉल करने के लिए भी परेशान नहीं करेगा। क्योंकि एक वस्तु में एक अलग Hashcode GUARANTEES अलग सामग्री।

    public static void main(String[] args) {

        Students S1 = new Students("taj",21);
        Students S2 = new Students("taj",21);

        System.out.println(S1.hashCode());
        System.out.println(S2.hashCode());

        HashMap<Students,String > HM = new HashMap<Students,String > (); 
        HM.put(S1, "tajinder");
        HM.put(S2, "tajinder");
        System.out.println(HM.size());
    }
}

Now lets change out main method a little bit. Output after this change is 

__ hash __

116201

__ hash __

116201

__ hash __

__ hash __

__ eq __

1
We can clearly see that equal method is called. Here is print statement __eq__, since we have same hashcode, then content of objects MAY or MAY not be similar. So program internally  calls Equal method to verify this. 


Conclusion 
If hashcode is different , equal method will not get called. 
if hashcode is same, equal method will get called.

Thanks , hope it helps. 

3

दो वस्तुएं समान हैं, तात्पर्य है कि उनके पास समान हैशकोड है, लेकिन इसके विपरीत नहीं।

2 समान वस्तुएं ------> उनके पास समान हैशकोड है

2 ऑब्जेक्ट्स में समान हैशकोड ---- xxxxx -> वे समान नहीं हैं

HashMap में जावा 8 अपडेट-

आप इस ऑपरेशन को अपने कोड में करते हैं -

myHashmap.put("old","old-value");
myHashMap.put("very-old","very-old-value");

इसलिए, मान लीजिए कि आपकी हैशकोड दोनों कुंजियों के लिए वापस आ गई है "old"और "very-old"समान है। तब फिर क्या होगा।

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

myHashmap.put("old","old-value");

तब हैशकोड "old"की गणना की जाती है, और क्योंकि हैशकोड बहुत बड़ा पूर्णांक भी हो सकता है, इसलिए, java ने आंतरिक रूप से ऐसा किया - (हैश यहाँ हैशकोड है और >>> सही शिफ्ट है)

hash XOR hash >>> 16

इसलिए एक बड़ी तस्वीर के रूप में देने के लिए, यह कुछ सूचकांक है, जो बीच होगी वापस आ जाएगी 0 15. करने के लिए अब आप अपने महत्वपूर्ण मूल्य जोड़ी "old"और"old-value" एंट्री ऑब्जेक्ट की कुंजी और वैल्यू इंस्टेंस वेरिएबल में बदल जाएगी। और फिर इस प्रविष्टि ऑब्जेक्ट को बाल्टी में संग्रहीत किया जाएगा, या आप कह सकते हैं कि किसी विशेष सूचकांक में, इस प्रविष्टि ऑब्जेक्ट को संग्रहीत किया जाएगा।

FYI- प्रवेश मानचित्र इंटरफ़ेस में एक वर्ग है- Map.Entry, इन हस्ताक्षर / परिभाषा के साथ

class Entry{
          final Key k;
          value v;
          final int hash;
          Entry next;
}

अब जब आप अगले कथन को निष्पादित करते हैं -

myHashmap.put("very-old","very-old-value");

और "very-old"जैसा कि एक ही हैशकोड देता है "old", इसलिए इस नए कुंजी मान युग्म को फिर से उसी सूचकांक या उसी बाल्टी में भेजा जाता है। लेकिन चूंकि यह बाल्टी खाली नहीं है, तो nextइस नई कुंजी मान जोड़ी को संग्रहीत करने के लिए एंट्री ऑब्जेक्ट के चर का उपयोग किया जाता है।

और इसे प्रत्येक वस्तु के लिए लिंक की गई सूची के रूप में संग्रहीत किया जाएगा, जिसमें समान हैशकोड है, लेकिन TRIEFY_THRESHOLD को मान 6 के साथ निर्दिष्ट किया गया है। इसलिए इसके पहुंचने के बाद, लिंक की गई सूची को पहले तत्व के रूप में संतुलित पेड़ (लाल-काला पेड़) में बदल दिया जाता है। जड़।


कमाल का जवाब (y)
सुधांशु गौर

2

प्रत्येक एंट्री ऑब्जेक्ट कुंजी-मूल्य जोड़ी का प्रतिनिधित्व करता है। यदि किसी बाल्टी में 1 से अधिक एंट्री है तो फील्ड नेक्स्ट एंट्री ऑब्जेक्ट को संदर्भित करता है।

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

ऐरे आकार 16 और डिफ़ॉल्ट 0.75 लोड संतुलन के साथ बनाया जाता है।

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

(स्रोत)


1

हैश मैप हैशिंग के सिद्धांत पर काम करता है

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

  • जब भी हम HashMap ऑब्जेक्ट पर कॉल (कुंजी k) विधि प्राप्त करते हैं। पहले यह जांचता है कि कुंजी शून्य है या नहीं। ध्यान दें कि HashMap में केवल एक नल कुंजी हो सकती है।

यदि कुंजी शून्य है, तो नल कुंजियाँ हमेशा हैश 0 पर मैप करती हैं, इस प्रकार इंडेक्स 0।

यदि कुंजी शून्य नहीं है, तो यह मुख्य ऑब्जेक्ट पर हैशंक्शन को कॉल करेगा, उपरोक्त विधि 4 को देखें। key.hashCode (), इसलिए key.hashCode () रिटर्न हैशवैल्यू के बाद, लाइन 4 दिखता है

            int hash = hash(hashValue)

और अब, यह अपने ही हैशिंग फ़ंक्शन में लौटे हैशवेल को लागू करता है।

हमें आश्चर्य हो सकता है कि हम हैश (हैशवैल्यू) का उपयोग करके फिर से हैवलव की गणना क्यों कर रहे हैं। उत्तर यह खराब गुणवत्ता वाले हैश कार्यों के विरुद्ध है।

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


1

मैं इस बात की जानकारी नहीं दूंगा कि हाशप कैसे काम करता है, लेकिन एक उदाहरण देगा ताकि हम यह याद रख सकें कि हशपैप वास्तविकता से संबंधित करके कैसे काम करता है।

हम कुंजी, मूल्य, हैशकोड और बाल्टी है।

कुछ समय के लिए, हम निम्नलिखित में से प्रत्येक के साथ संबंधित होंगे:

  • बाल्टी -> एक समाज
  • हैशकोड -> सोसायटी का पता (अद्वितीय हमेशा)
  • मूल्य -> ​​सोसायटी में एक घर
  • कुंजी -> घर का पता।

Map.get (कुंजी) का उपयोग करना:

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

  • SSN सोसायटी का नाम
  • 92313 (जोसेफ) - JavaLovers
  • 13214 - एंगुलर जेएसएलओवर
  • 98080 - जावालोवर्स
  • 53808 - जीवविज्ञान

  1. यह SSN (कुंजी) पहले हमें एक हैशकोड (इंडेक्स टेबल से) देता है जो कि सोसायटी के नाम के अलावा कुछ नहीं है।
  2. अब, घरों का निर्माण एक ही समाज में हो सकता है, इसलिए हैशकोड सामान्य हो सकता है।
  3. मान लीजिए, सोसाइटी दो घरों के लिए आम है, हम कैसे पहचान करने जा रहे हैं कि हम किस घर में जा रहे हैं, हाँ, (SSN) कुंजी का उपयोग करके जो कि घर के पते के अलावा कुछ भी नहीं है

Map.put का उपयोग करना (कुंजी, मूल्य)

यह HashCode को खोजकर इस मान के लिए एक उपयुक्त समाज का पता लगाता है और फिर मान संग्रहीत किया जाता है।

मुझे उम्मीद है कि यह मदद करता है और यह संशोधनों के लिए खुला है।


0

यह एक लंबा जवाब होने वाला है, एक ड्रिंक को पकड़ो और पढ़ें ...

हैशिंग स्मृति में एक महत्वपूर्ण-मूल्य जोड़ी को संग्रहीत करने के बारे में है जिसे तेजी से पढ़ा और लिखा जा सकता है। यह एक सरणी में कुंजियाँ और एक लिंक्डलिस्ट में मूल्यों को संग्रहीत करता है।

कहते हैं कि मैं 4 प्रमुख मूल्य जोड़े स्टोर करना चाहता हूं -

{
girl => ahhan , 
misused => Manmohan Singh , 
horsemints => guess what”, 
no => way
}

इसलिए कुंजियों को संग्रहीत करने के लिए हमें 4 तत्व की एक सरणी की आवश्यकता होती है। अब मैं इन 4 चाबियों में से एक को 4 सरणी इंडेक्स (0,1,2,3) में कैसे मैप करूं?

इसलिए जावा व्यक्तिगत चाबियों का हैशकोड ढूंढता है और उन्हें एक विशेष सरणी इंडेक्स में मैप करता है। हैशकोड सूत्र है -

1) reverse the string.

2) keep on multiplying ascii of each character with increasing power of 31 . then add the components .

3) So hashCode() of girl would be –(ascii values of  l,r,i,g are 108, 114, 105 and 103) . 

e.g. girl =  108 * 31^0  + 114 * 31^1  + 105 * 31^2 + 103 * 31^3  = 3173020

हैश और लड़की !! मुझे पता है कि आप क्या सोच रहे हैं। उस जंगली युगल के बारे में आपके आकर्षण ने आपको एक महत्वपूर्ण बात याद दिला दी।

जावा को 31 से गुणा क्यों करें?

ऐसा इसलिए है, क्योंकि 31 ^ 5 - 1 के रूप में 31 एक विषम प्राइम है। और विषम प्रधान हाश टकराव की संभावना को कम करता है

अब इस हैश कोड को एक सरणी सूचकांक में कैसे मैप किया जाता है?

जवाब है , Hash Code % (Array length -1) । तो हमारे मामले में “girl”मैप किया गया (3173020 % 3) = 1है। जो सरणी का दूसरा तत्व है।

और मान "आहान" एरे इंडेक्स 1 से जुड़े लिंक्डलिस्ट में संग्रहीत है।

HashCollision - यदि आप hasHCodeकुंजियों को खोजने की कोशिश करते हैं “misused”और “horsemints”ऊपर वर्णित सूत्रों का उपयोग करके आप दोनों को एक समान देते हुए देखेंगे1069518484 । Whooaa !! सबक सीखना -

2 समान ऑब्जेक्ट में समान हैशकोड होना चाहिए लेकिन हैशकोड से मेल खाता है तो ऑब्जेक्ट समान हैं। तो इसे "दुरुपयोग" और "घुड़सवार" के समान दोनों मानों को बाल्टी 1 (1069518484% 3) में संग्रहीत करना चाहिए।

अब हैश मैप जैसा दिखता है -

Array Index 0 
Array Index 1 - LinkedIst (“ahhan , Manmohan Singh , guess what”)
Array Index 2  LinkedList (“way”)
Array Index 3  

अब अगर कुछ निकाय कुंजी के लिए मान ज्ञात करने का प्रयास करते हैं “horsemints”, तो जावा जल्दी से इसका हैशकोड पा लेगा, इसे मॉड्यूल कर देगा और इसके लिए लिंक्डलिस्ट में मान खोजना शुरू कर देगाindex 1 । तो इस तरह से हमें सभी 4 सरणी इंडेक्स की खोज करने की आवश्यकता नहीं है, जिससे डेटा एक्सेस तेजी से हो सके।

लेकिन, रुको, एक सेकंड। उस लिंक्डलिस्ट में एरियर इंडेक्स 1 में 3 मान हैं, यह कैसे पता चलता है कि कुंजी "हॉर्स मार्क" के लिए कौन सा मूल्य था?

वास्तव में मैंने झूठ बोला था, जब मैंने कहा था कि हाशप सिर्फ लिंक्डलिस्ट में मूल्यों को संग्रहीत करता है।

यह मानचित्र प्रविष्टि के रूप में दोनों प्रमुख मूल्य जोड़े को संग्रहीत करता है। तो वास्तव में मानचित्र इस तरह दिखता है।

Array Index 0 
Array Index 1 - LinkedIst (<”girl => ahhan”> , <” misused => Manmohan Singh”> , <”horsemints => guess what”>)
Array Index 2  LinkedList (<”no => way”>)
Array Index 3  

अब आप देख सकते हैं कि ArrayIndex1 से जुड़े लिंकलिस्ट के माध्यम से ट्रेस करने के दौरान यह वास्तव में उस लिंक्डलिस्ट के प्रत्येक प्रविष्टि की कुंजी की तुलना "हॉर्समिंटस" से करता है और जब वह पाता है तो वह इसका मान लौटाता है।

आशा है कि इसे पढ़ते हुए आपको मज़ा आया होगा :)


मुझे लगता है कि यह गलत है: "यह एक लिंक में कुंजियों और मूल्यों को एक लिंक्डलिस्ट में संग्रहीत करता है।"
एसीवी

प्रत्येक बाल्टी के लिए सूची में प्रत्येक तत्व में कुंजी और मूल्य के साथ-साथ अगले नोड का संदर्भ भी शामिल है।
एसीवी

0

जैसा कि कहा जाता है, एक तस्वीर 1000 शब्दों के लायक है। मैं कहता हूं: कुछ कोड 1000 शब्दों से बेहतर है। यहाँ HashMap का स्रोत कोड है। विधि प्राप्त करें:

/**
     * Implements Map.get and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

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

आइये देखते हैं put()विधि:

  /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don't change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

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

i = (n - 1) & hashयहां iवह इंडेक्स है जहां नया तत्व डाला जाएगा (या यह "बकेट" है)।ntabसरणी का आकार ("बाल्टियाँ") है।

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

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