.NET में IEHalityComparer <T> में GetHashCode की भूमिका क्या है?


142

मैं इंटरफ़ेस IEqualityComparer के GetHashCode विधि की भूमिका को समझने की कोशिश कर रहा हूं।

निम्न उदाहरण MSDN से लिया गया है:

using System;
using System.Collections.Generic;
class Example {
    static void Main() {
        try {

            BoxEqualityComparer boxEqC = new BoxEqualityComparer();

            Dictionary<Box, String> boxes = new Dictionary<Box,
                                                string>(boxEqC);

            Box redBox = new Box(4, 3, 4);
            Box blueBox = new Box(4, 3, 4);

            boxes.Add(redBox, "red");
            boxes.Add(blueBox, "blue");

            Console.WriteLine(redBox.GetHashCode());
            Console.WriteLine(blueBox.GetHashCode());
        }
        catch (ArgumentException argEx) {

            Console.WriteLine(argEx.Message);
        }
    }
}

public class Box {
    public Box(int h, int l, int w) {
        this.Height = h;
        this.Length = l;
        this.Width = w;
    }
    public int Height { get; set; }
    public int Length { get; set; }
    public int Width { get; set; }
}

class BoxEqualityComparer : IEqualityComparer<Box> {

    public bool Equals(Box b1, Box b2) {
        if (b1.Height == b2.Height & b1.Length == b2.Length
                            & b1.Width == b2.Width) {
            return true;
        }
        else {
            return false;
        }
    }

    public int GetHashCode(Box bx) {
        int hCode = bx.Height ^ bx.Length ^ bx.Width;
        return hCode.GetHashCode();
    }
}

दो बॉक्स ऑब्जेक्ट्स की तुलना करने के लिए समान पद्धति का कार्यान्वयन पर्याप्त नहीं होना चाहिए? यह वह जगह है जहां हम रूपरेखा को वस्तुओं की तुलना करने के लिए इस्तेमाल किया जाने वाला नियम बताते हैं। GetHashCode की आवश्यकता क्यों है?

धन्यवाद।

लुसियान


पढ़ें: en.wikipedia.org/wiki/Hash_table फिर देखें कि क्या आप GetHashCode के उद्देश्य को बेहतर ढंग से समझते हैं।
खर्च करें

1
इस शानदार उत्तर को देखें: stackoverflow.com/a/3719802/136967
मिखाइल

जवाबों:


201

पहले पृष्ठभूमि का एक सा ...

.NET में प्रत्येक ऑब्जेक्ट में एक समान विधि और एक GetHashCode विधि है।

समान वस्तु का उपयोग किसी वस्तु को किसी अन्य वस्तु से तुलना करने के लिए किया जाता है - यह देखने के लिए कि क्या दोनों वस्तुएं समान हैं।

GetHashCode विधि ऑब्जेक्ट का 32-बिट पूर्णांक प्रतिनिधित्व उत्पन्न करता है। चूंकि किसी वस्तु में कितनी जानकारी हो सकती है, इसकी कोई सीमा नहीं है, कुछ हैश कोड कई वस्तुओं द्वारा साझा किए जाते हैं - इसलिए हैश कोड अद्वितीय नहीं है।

एक डिक्शनरी वास्तव में एक अच्छा डेटा स्ट्रक्चर है जो ऐड / रिमूव / गेट ऑपरेशंस के लिए निरंतर लागत के बदले में एक उच्चतर स्मृति पदचिह्न देता है। यह हालांकि पर चलने के लिए एक गरीब विकल्प है। आंतरिक रूप से, एक शब्दकोश में बाल्टी की एक सरणी होती है, जहां मूल्यों को संग्रहीत किया जा सकता है। जब आप एक शब्दकोश में एक कुंजी और मूल्य जोड़ते हैं, तो GetHashCode विधि को कुंजी पर बुलाया जाता है। लौटाए गए हैशकोड का उपयोग बाल्टी के सूचकांक को निर्धारित करने के लिए किया जाता है जिसमें कुंजी / मूल्य जोड़ी को संग्रहीत किया जाना चाहिए।

जब आप मान को एक्सेस करना चाहते हैं, तो आप फिर से की में पास होते हैं। GetHashCode विधि को कुंजी पर कहा जाता है, और बाल्टी जिसमें मान स्थित होता है।

जब एक IEqualityComparer को डिक्शनरी के निर्माता के रूप में पारित किया जाता है, तो IEqualityComparer.Equals और IEqualityComparer.GetHashCode विधियों का उपयोग मुख्य ऑब्जेक्ट्स पर विधियों के बजाय किया जाता है।

अब यह समझाने के लिए कि दोनों विधियाँ क्यों आवश्यक हैं, इस उदाहरण पर विचार करें:

BoxEqualityComparer boxEqC = new BoxEqualityComparer(); 

Dictionary<Box, String> boxes = new Dictionary<Box, string>(boxEqC); 

Box redBox = new Box(100, 100, 25);
Box blueBox = new Box(1000, 1000, 25);

boxes.Add(redBox, "red"); 
boxes.Add(blueBox, "blue"); 

अपने उदाहरण में BoxEqualityComparer.GetHashCode पद्धति का उपयोग करते हुए, इन दोनों बॉक्सों में समान हैशकोड - 100 ^ 100 ^ 25 = 1000 ^ 1000 ^ 25 = 25 - भले ही वे स्पष्ट रूप से समान वस्तु नहीं हैं। इस मामले में वे एक ही हैशकोड हैं इसका कारण यह है कि आप ^ (बिटवाइज एक्सक्लूसिव-ओआर) ऑपरेटर का उपयोग कर रहे हैं, इसलिए 100 ^ 100 कैंसिल शून्य छोड़ कर, जैसा कि 1000 ^ 1000 करता है। जब दो अलग-अलग वस्तुओं की एक ही कुंजी होती है, तो हम उस टकराव को कहते हैं।

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

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

TLDR

इसलिए सारांश में, GetHashCode पद्धति का उपयोग उस पते को उत्पन्न करने के लिए किया जाता है जहां ऑब्जेक्ट संग्रहीत होता है। तो एक शब्दकोश को इसके लिए खोज नहीं करनी होगी। यह बस हैशकोड की गणना करता है और उस स्थान पर कूदता है। बराबरी का तरीका समानता का एक बेहतर परीक्षण है, लेकिन इसका उपयोग किसी पते के स्थान में ऑब्जेक्ट को मैप करने के लिए नहीं किया जा सकता है।


4
उन लोगों के लिए, ^ -operator क्या है, यह सोचकर, यह बिटवाइज़ एक्सक्लूसिव-OR ऑपरेटर है, देखें msdn.microsoft.com/en-us/library/zkacc7k1.aspx
आर। श्रेयर्स

2
बस इसे स्पष्ट रूप से इंगित करने के लिए: ( msdn.microsoft.com/en-us/library/ms132155.aspx ) कार्यान्वयनकर्ताओं को नोट लागू करना यह सुनिश्चित करने के लिए आवश्यक है कि यदि समान विधि दो ऑब्जेक्ट्स x और y के लिए सही हो, तो मान लौटा। x के लिए GetHashCode विधि द्वारा y के लिए दिए गए मान के बराबर होना चाहिए।
डिएगो फ्रीनर

2
@DiegoFrehner - आप काफी सही हैं। एक और चीज जो लोगों की यात्रा कर सकती है वह यह है कि GetHashCode पद्धति का मान ऑब्जेक्ट संशोधित होने पर भिन्न नहीं होना चाहिए। तो GetHashCode पर निर्भर करता है कि वस्तु के भीतर क्षेत्रों पर आसानी से पढ़ा जा सकता है (अपरिवर्तनीय)। यहाँ एक स्पष्टीकरण है: stackoverflow.com/a/4868940/469701
sheikhjabootie

1
@ आर्कटिक: किसी वस्तु का हैश कोड तब तक नहीं बदलना चाहिए जब तक कि वह फैशन में परिवर्तित न हो जाए जो समानता को प्रभावित करता है। यदि किसी वर्ग को समानता को प्रभावित करने के लिए इस तरह के फैशन में उत्परिवर्तित किया जा सकता है, तो कोड को किसी भी उदाहरण में संग्रहीत करने से बचना चाहिए जो कोड के संपर्क में हो सकता है जो कि शब्दकोश में होने पर इसे म्यूट कर देगा। यदि कोड जो ऑब्जेक्ट को स्टोर करता है तो उस नियम का पालन करता है, जिसमें हैश कोड होता है जो कि परिवर्तनशील स्थिति को दर्शाता है। यह बहुत बुरा है। NET राज्य की समानता और समानता को बेहतर ढंग से अलग नहीं करता है, क्योंकि दोनों उपयोगी अवधारणाएं हैं।
सुपरकाट

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

9

GetHashCode का उपयोग डिक्शनरी कोलेशन में किया जाता है और यह इसमें ऑब्जेक्ट्स को स्टोर करने के लिए हैश बनाता है। यहाँ एक अच्छा लेख क्यों और कैसे उपयोग करने के लिए है IEqualtyComparer और GetHashCode http://dotnetperls.com/iequalitycomparer


4
अधिक: यदि आपको बराबरी की तुलना करने की आवश्यकता है , तो यह लागू होगा, लेकिन जब आपको शब्दकोश से तत्व प्राप्त करने की आवश्यकता होती है, तो हैश द्वारा ऐसा करना आसान होता है, बराबरी का उपयोग करके नहीं ।
Ash

5

हालांकि यह संभव Dictionary<TKey,TValue>है कि हर एक संग्रहीत कुंजी पर इसके GetValueऔर इसी तरह के तरीकों Equalsको देखने के लिए कहा जाए कि क्या यह मांगी जा रही चीज़ों से मेल खाता है या नहीं, यह बहुत धीमा होगा। इसके बजाय, कई हैश-आधारित संग्रहों की तरह, यह GetHashCodeविचार से अधिकांश गैर-मिलान मूल्यों को जल्दी से बाहर करने पर निर्भर करता है । यदि GetHashCodeकिसी आइटम पर कॉल किया जा रहा है तो पैदावार 42 हो सकती है, और संग्रह में 53,917 आइटम हैं, लेकिन कॉलिंगGetHashCode करने से ४२ के अलावा एक मूल्य प्राप्त होता है, तो केवल ३ वस्तुओं की तुलना की जा रही है। अन्य 53,914 को सुरक्षित रूप से अनदेखा किया जा सकता है।

कारण एक GetHashCodeएक में शामिल है IEqualityComparer<T>संभावना है कि एक शब्दकोश के उपभोक्ता बराबर वस्तुओं कि सामान्य रूप से होता है के रूप में संबंध के लिए चाहते हो सकता है के लिए अनुमति देने के लिए है नहीं बराबर के रूप में एक दूसरे को मानते हैं। सबसे आम उदाहरण एक कॉलर होगा जो स्ट्रिंग्स को कुंजियों के रूप में उपयोग करना चाहता है लेकिन केस-असंवेदनशील तुलना का उपयोग करता है। उस कार्य को कुशलता से करने के लिए, शब्दकोश को कुछ प्रकार के हैश फ़ंक्शन की आवश्यकता होगी जो "फॉक्स" और "फॉक्स" के लिए समान मूल्य प्राप्त करेंगे, लेकिन उम्मीद है कि "बॉक्स" या "ज़ेबरा" के लिए कुछ और मिलेगा। चूंकि GetHashCodeविधि में निर्मितString उस तरह से काम नहीं करती है, शब्दकोश को कहीं और से ऐसी विधि प्राप्त करने की आवश्यकता होगी,IEqualityComparer<T>Equals विधि जो "फॉक्स" और "फॉक्स" को एक-दूसरे के समान समझती है, लेकिन "बॉक्स" या "ज़ेबरा" नहीं।


प्रश्न का सही और बिंदु उत्तर! GetHashCode () को विचाराधीन वस्तुओं के लिए बराबर () पूरक करना है।
सुमित

@ सुमित: हैशिंग की कई चर्चाएं बाल्टियों के बारे में बात करती हैं, लेकिन मुझे लगता है कि बहिष्कार के बारे में सोचना अधिक उपयोगी है। यदि तुलना महंगी होती है, तो हैशिंग उन लाभों का प्रस्ताव दे सकते हैं, जब वे संग्रह का उपयोग कर रहे हैं, जो बाल्टी में व्यवस्थित नहीं हैं।
14
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.