एक संग्रह के लिए हैशकोड विधि के लिए सर्वश्रेष्ठ कार्यान्वयन


299

हम hashCode()एक संग्रह के लिए विधि के सर्वोत्तम कार्यान्वयन पर कैसे निर्णय लेते हैं (यह मानते हुए कि विधि समान रूप से ओवरराइड की गई है)?


2
जावा 7+ के साथ, मुझे लगता है कि Objects.hashCode(collection)एक सही समाधान होना चाहिए!
डियाब्लो

3
@ डियाब्लो मुझे नहीं लगता कि इस सवाल का जवाब बिल्कुल भी नहीं है - वह तरीका बस लौटता है collection.hashCode()( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96d96a0f/src/share/… )
cbreezier

जवाबों:


438

सबसे अच्छा कार्यान्वयन? यह एक कठिन प्रश्न है क्योंकि यह उपयोग पैटर्न पर निर्भर करता है।

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

एक छोटा संस्करण

  1. एक बनाएँ int resultऔर एक गैर-शून्य मान असाइन करें ।

  2. विधि में परीक्षण किए गए प्रत्येक क्षेत्र के लिए , एक हैश कोड की गणना करें :fequals()c

    • यदि फ़ील्ड f एक है boolean: गणना (f ? 0 : 1);
    • क्षेत्र च है एक byte, char, shortया int: calculate (int)f;
    • यदि फ़ील्ड f एक है long: गणना (int)(f ^ (f >>> 32));
    • यदि फ़ील्ड f एक है float: गणना Float.floatToIntBits(f);
    • यदि फ़ील्ड f एक है double: Double.doubleToLongBits(f)हर लंबे मूल्य की तरह वापसी मूल्य की गणना और संभाल;
    • यदि फ़ील्ड f एक ऑब्जेक्ट है : hashCode()विधि के परिणाम का उपयोग करें या यदि 0 f == null;
    • यदि फ़ील्ड f एक सरणी है : प्रत्येक फ़ील्ड को अलग तत्व के रूप में देखें और पुनरावर्ती फैशन में हैश मान की गणना करें और अगले वर्णित मानों को संयोजित करें।
  3. के cसाथ हैश मान को मिलाएं result:

    result = 37 * result + c
  4. वापसी result

इसके परिणामस्वरूप अधिकांश उपयोग स्थितियों के लिए हैश मानों का उचित वितरण होना चाहिए।


45
हाँ, मैं विशेष रूप से उत्सुक हूँ कि 37 नंबर कहाँ से आता है।
कीप

17
मैंने जोश बलोच की "प्रभावी जावा" पुस्तक के आइटम 8 का उपयोग किया।
डेमिस्टर

39
@dma_k प्राइम नंबर और इस उत्तर में वर्णित विधि का उपयोग करने का कारण यह सुनिश्चित करना है कि गणना की गई हैशकोड अद्वितीय होगी । गैर-अभाज्य संख्याओं का उपयोग करते समय, आप इसकी गारंटी नहीं दे सकते। इससे कोई फर्क नहीं पड़ता कि आप किस प्रमुख संख्या को चुनते हैं, संख्या 37 के बारे में कुछ भी जादुई नहीं है (बहुत बुरा 42 एक अभाज्य संख्या नहीं है, एह?)
साइमन फोर्सबर्ग

34
@ साइमनएंड्रॉफर्सबर्ग, गणना की गई हैश कोड हमेशा अद्वितीय नहीं हो सकता है :) एक हैशकोड है। हालाँकि मुझे यह विचार आया: अभाज्य संख्या में केवल एक गुणक होता है, जबकि अभाज्य संख्या में कम से कम दो होते हैं। यह गुणन ऑपरेटर के लिए एक अतिरिक्त संयोजन बनाता है जिसके परिणामस्वरूप समान हैश होता है, अर्थात टकराव होता है।
dma_k


140

यदि आप dmeister द्वारा सुझाए गए प्रभावी जावा कार्यान्वयन से खुश हैं, तो आप अपना स्वयं का रोल करने के बजाय लाइब्रेरी कॉल का उपयोग कर सकते हैं:

@Override
public int hashCode() {
    return Objects.hashCode(this.firstName, this.lastName);
}

इसके लिए com.google.common.base.Objects.hashCodeजावा 7 में या तो अमरूद ( ) या मानक पुस्तकालय की आवश्यकता होती है, java.util.Objects.hashलेकिन उसी तरह काम करता है।


8
जब तक किसी के पास इनका उपयोग न करने का एक अच्छा कारण है, तब तक किसी भी मामले में इनका उपयोग जरूर करना चाहिए। (इसे और मजबूत बनाना, क्योंकि यह IMHO तैयार किया जाना चाहिए।) मानक कार्यान्वयन / पुस्तकालयों का उपयोग करने के लिए विशिष्ट तर्क (सर्वोत्तम प्रथाओं, अच्छी तरह से परीक्षण किए गए, कम त्रुटि प्रवण, आदि)।
Kissaki

7
@ justin.hughey आप भ्रमित होने लगते हैं। एकमात्र ऐसा मामला जो आपको ओवरराइड करना चाहिए hashCode, यदि आपके पास कोई रिवाज है equals, और यह वही है जो इन लाइब्रेरी विधियों के लिए डिज़ाइन किया गया है। प्रलेखन उनके संबंध में उनके व्यवहार पर काफी स्पष्ट है equals। एक पुस्तकालय कार्यान्वयन आपको यह जानने से वंचित करने का दावा नहीं करता है कि एक सही hashCodeक्रियान्वयन की विशेषताएं क्या हैं - ये पुस्तकालय आपके लिए उन मामलों के बहुमत के लिए इस तरह के अनुरूप कार्यान्वयन को लागू करना आसान बनाते हैं जहां equalsयह अधिक महत्वपूर्ण है।
बेकर

6
Java.util.Objects वर्ग को देखने वाले किसी भी एंड्रॉइड डेवलपर्स के लिए, इसे केवल एपीआई 19 में पेश किया गया था, इसलिए सुनिश्चित करें कि आप किटकैट पर या इससे ऊपर चल रहे हैं अन्यथा आपको NoClassDefFoundError मिलेगा।
एंड्रयू केली

3
सबसे अच्छा जवाब IMO, हालांकि उदाहरण के माध्यम से मैंने java.util.Objects.hash(...)अमरूद com.google.common.base.Objects.hashCode(...)विधि के बजाय JDK7 विधि को चुना होगा । मुझे लगता है कि ज्यादातर लोग एक अतिरिक्त निर्भरता पर मानक पुस्तकालय का चयन करेंगे।
माल्टे स्कॉरप्पा

2
यदि दो तर्क या अधिक हैं और यदि उनमें से कोई एक सरणी है, तो परिणाम वह नहीं हो सकता है जो आप अपेक्षा करते हैं क्योंकि hashCode()एक सरणी के लिए यह बस है java.lang.System.identityHashCode(...)
स्टारकिफ

59

एक्लिप्स द्वारा प्रदान की गई कार्यक्षमता का उपयोग करना बेहतर है जो एक बहुत अच्छा काम करता है और आप अपने तर्क और ऊर्जा को व्यापार तर्क विकसित करने में लगा सकते हैं।


4
+1 एक अच्छा व्यावहारिक समाधान। डमीस्टर का समाधान अधिक व्यापक है, लेकिन जब मैं खुद को हैशकोड लिखने की कोशिश करता हूं, तो मैं नल को संभालना भूल जाता हूं।
क्वांटम

1
+1 क्वांटम 7 से सहमत हैं, लेकिन मैं कहूंगा कि यह समझना भी बहुत अच्छा है कि ग्रहण-जनित कार्यान्वयन क्या कर रहा है, और इसे इसके क्रियान्वयन का विवरण कहाँ से मिलता है।
jwir3

15
क्षमा करें, लेकिन "कुछ आईडीई द्वारा प्रदान की गई कार्यक्षमता" से जुड़े उत्तर वास्तव में प्रोग्रामिंग भाषा के संदर्भ में प्रासंगिक नहीं हैं। दर्जनों आईडीई हैं और यह सवाल का जवाब नहीं देता है ... क्योंकि यह एल्गोरिथम निर्धारण के बारे में अधिक है और सीधे समान () कार्यान्वयन से जुड़ा है - एक आईडीई के बारे में कुछ भी नहीं पता होगा।
डारेल टीग

57

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

@Override 
public int hashCode() {

    // Start with a non-zero constant. Prime is preferred
    int result = 17;

    // Include a hash for each field.

    // Primatives

    result = 31 * result + (booleanField ? 1 : 0);                   // 1 bit   » 32-bit

    result = 31 * result + byteField;                                // 8 bits  » 32-bit 
    result = 31 * result + charField;                                // 16 bits » 32-bit
    result = 31 * result + shortField;                               // 16 bits » 32-bit
    result = 31 * result + intField;                                 // 32 bits » 32-bit

    result = 31 * result + (int)(longField ^ (longField >>> 32));    // 64 bits » 32-bit

    result = 31 * result + Float.floatToIntBits(floatField);         // 32 bits » 32-bit

    long doubleFieldBits = Double.doubleToLongBits(doubleField);     // 64 bits (double) » 64-bit (long) » 32-bit (int)
    result = 31 * result + (int)(doubleFieldBits ^ (doubleFieldBits >>> 32));

    // Objects

    result = 31 * result + Arrays.hashCode(arrayField);              // var bits » 32-bit

    result = 31 * result + referenceField.hashCode();                // var bits » 32-bit (non-nullable)   
    result = 31 * result +                                           // var bits » 32-bit (nullable)   
        (nullableReferenceField == null
            ? 0
            : nullableReferenceField.hashCode());

    return result;

}

संपादित करें

आमतौर पर, जब आप ओवरराइड करते हैं, तो आप ओवरराइड hashcode(...)भी करना चाहते हैं equals(...)। तो उन लोगों के लिए जो पहले से ही लागू हैं या लागू किए गए हैं equals, यहां मेरे गिठुब से एक अच्छा संदर्भ है ...

@Override
public boolean equals(Object o) {

    // Optimization (not required).
    if (this == o) {
        return true;
    }

    // Return false if the other object has the wrong type, interface, or is null.
    if (!(o instanceof MyType)) {
        return false;
    }

    MyType lhs = (MyType) o; // lhs means "left hand side"

            // Primitive fields
    return     booleanField == lhs.booleanField
            && byteField    == lhs.byteField
            && charField    == lhs.charField
            && shortField   == lhs.shortField
            && intField     == lhs.intField
            && longField    == lhs.longField
            && floatField   == lhs.floatField
            && doubleField  == lhs.doubleField

            // Arrays

            && Arrays.equals(arrayField, lhs.arrayField)

            // Objects

            && referenceField.equals(lhs.referenceField)
            && (nullableReferenceField == null
                        ? lhs.nullableReferenceField == null
                        : nullableReferenceField.equals(lhs.nullableReferenceField));
}

1
एंड्रॉइड डॉक्यूमेंटेशन में अब उपरोक्त कोड शामिल नहीं है, इसलिए यहां वेबैक मशीन
क्रिस्टोफर रुकिंस्की

17

पहले सुनिश्चित करें कि समान रूप से सही तरीके से लागू किया गया है। से एक आईबीएम डेवलपर लेख :

  • समरूपता: दो संदर्भों के लिए, a और b, a .equals (b) यदि और केवल यदि b.equals (a)
  • संवेदनशीलता: सभी गैर-शून्य संदर्भों के लिए, a.equals (a)
  • परिवर्तनशीलता: यदि a.equals (b) और b.equals (c), तो a.equals (c)

फिर सुनिश्चित करें कि हैशकोड के साथ उनके संबंध संपर्क का सम्मान करते हैं (उसी लेख से):

  • हैशकोड () के साथ संगति: दो समान वस्तुओं में समान हैशकोड () मान होना चाहिए

अंत में एक अच्छा हैश फ़ंक्शन आदर्श हैश फ़ंक्शन को देखने का प्रयास करना चाहिए ।


11

about8.blogspot.com, आपने कहा

यदि बराबर () दो वस्तुओं के लिए सही है, तो हैशकोड () को समान मान लौटना चाहिए। यदि बराबर () गलत है, तो हैशकोड () अलग-अलग मान वापस करना चाहिए

मैं आपसे सहमत नहीं हो सकता। यदि दो वस्तुओं में समान हैशकोड होता है तो इसका मतलब यह नहीं है कि वे समान हैं।

यदि A, B के बराबर है, तो A.Ashcode B.hascode के बराबर होना चाहिए

परंतु

यदि A.hashcode B.hascode के बराबर है तो इसका मतलब यह नहीं है कि A को B के बराबर होना चाहिए


3
अगर (A != B) and (A.hashcode() == B.hashcode()), इसे हम हैश फंक्शन की टक्कर कहते हैं। ऐसा इसलिए है क्योंकि हैश फ़ंक्शन का कोडोमैन हमेशा परिमित रहता है, जबकि यह डोमेन आमतौर पर नहीं होता है। कोडोमैन जितना बड़ा होता है, उतनी बार टकराव कम होना चाहिए। अच्छे हैश फंक्शन को अलग-अलग ऑब्जेक्ट्स के लिए अलग-अलग हैश को लौटना चाहिए, जिसमें सबसे बड़ी संभावना प्राप्त की गई विशेष कोडोमेन साइज है। यह शायद ही कभी पूरी तरह से गारंटी दी जा सकती है।
Krzysztof Jabłoński

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

अच्छी टिप्पणियाँ लेकिन 'अलग-अलग वस्तुओं' शब्द का उपयोग करने के बारे में सावधान रहें ... क्योंकि बराबर () और इस प्रकार हैशकोड () कार्यान्वयन आवश्यक रूप से एक OO संदर्भ में विभिन्न वस्तुओं के बारे में नहीं हैं, लेकिन आमतौर पर उनके डोमेन मॉडल अभ्यावेदन (जैसे, दो) के बारे में अधिक हैं लोगों को एक ही माना जा सकता है यदि वे एक देश कोड और देश आईडी साझा करते हैं - हालांकि ये एक जेवीएम में दो अलग-अलग 'ऑब्जेक्ट' हो सकते हैं - उन्हें 'बराबर' माना जाता है और दिए गए हैशकोड) ...
डेरेल टीग

7

यदि आप ग्रहण का उपयोग करते हैं, तो आप उत्पन्न equals()और hashCode()उपयोग कर सकते हैं :

स्रोत -> हैशकोड उत्पन्न करें () और बराबर ()।

इस फ़ंक्शन का उपयोग करके आप यह तय कर सकते हैं कि आप किन क्षेत्रों में समानता और हैश कोड गणना के लिए उपयोग करना चाहते हैं, और एक्लिप्स इसी तरीके को उत्पन्न करता है।


7

Apache Commons Lang में प्रभावी Java के तर्क hashcode()और equals()तर्क का अच्छा कार्यान्वयन है । चेकआउट हैशकोडबाउटल और इक्वल्सबाली


1
इस API का नकारात्मक पक्ष यह है कि जब भी आप समान और हैशकोड कहते हैं, तब तक आप ऑब्जेक्ट निर्माण की लागत का भुगतान करते हैं (जब तक कि आपकी वस्तु अपरिवर्तनीय नहीं है और आप हैश को रोकते हैं), जो कुछ मामलों में बहुत कुछ हो सकता है।
जेम्स मैकमोहन

यह मेरा पसंदीदा दृष्टिकोण था, हाल तक। मैंने SharedKey OneToOne एसोसिएशन के लिए एक मापदंड का उपयोग करते हुए StackOverFlowError में भाग लिया है। अधिक से, Objectsवर्ग Java7 पर से प्रदान करता है hash(Object ..args)और equals()तरीके। इन्हें jdk 1.7+
Diablo

@ डियाब्लो मुझे लगता है, आपकी समस्या ऑब्जेक्ट ग्राफ में एक चक्र थी और तब आप सबसे अधिक कार्यान्वयन के साथ भाग्य से बाहर हो जाते हैं क्योंकि आपको कुछ संदर्भों को अनदेखा करने या चक्र को तोड़ने की आवश्यकता होती है (ए IdentityHashMap) को कम करना। FWIW I एक आईडी-आधारित हैशकोड का उपयोग करता है और सभी संस्थाओं के लिए बराबर है।
मातरिनस

6

अन्य अधिक विस्तृत उत्तर (कोड की अवधि में) को पूरा करने के लिए बस एक त्वरित नोट:

यदि मैं इस प्रश्न पर विचार करता हूं कि कैसे-कैसे-मैं-एक-हैश-टेबल-इन-जावा और विशेष रूप से jGuru FAQ प्रविष्टि , तो मुझे विश्वास है कि कुछ अन्य मानदंड हैं जिन पर एक हैश कोड का न्याय किया जा सकता है:

  • तुल्यकालन (क्या अहंकार समवर्ती अभिगम का समर्थन करता है या नहीं)?
  • सुरक्षित पुनरावृत्ति को विफल करें (क्या अहंकार एक संग्रह का पता लगाता है जो पुनरावृत्ति के दौरान बदलता है)
  • शून्य मान (हैश कोड संग्रह में शून्य मान का समर्थन करता है)

4

यदि मैं आपके प्रश्न को सही ढंग से समझता हूं, तो आपके पास एक कस्टम कलेक्शन क्लास है (यानी एक नया वर्ग जो संग्रह इंटरफ़ेस से निकलता है) और आप हैशकोड () पद्धति को लागू करना चाहते हैं।

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

   public int hashCode() {
      int hashCode = 1;
      Iterator i = iterator();
      while (i.hasNext()) {
        Object obj = i.next();
        hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
      }
  return hashCode;
   }

अब यदि आप जो चाहते हैं, वह विशिष्ट वर्ग के लिए हैश कोड की गणना करने का सबसे अच्छा तरीका है, तो मैं आम तौर पर सभी फ़ील्ड्स को संसाधित करने के लिए ^ (बिटवाइड एक्सक्लूसिव या) ऑपरेटर का उपयोग करता हूं जो कि बराबर पद्धति में उपयोग करते हैं:

public int hashCode(){
   return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}

2

@ about8: वहाँ एक बहुत गंभीर बग है।

Zam obj1 = new Zam("foo", "bar", "baz");
Zam obj2 = new Zam("fo", "obar", "baz");

वही हैशकोड

आप शायद ऐसा कुछ चाहते हैं

public int hashCode() {
    return (getFoo().hashCode() + getBar().hashCode()).toString().hashCode();

(क्या आप इन दिनों सीधे जावा में int से हैशकोड प्राप्त कर सकते हैं? मुझे लगता है कि यह कुछ आटोकास्टिंग करता है .. अगर ऐसा है, तो स्ट्रैस्ट्रिंग को छोड़ें, यह बदसूरत है।)


3
बग लगभग 8.blogspot.com द्वारा लंबे उत्तर में है - स्ट्रिंग्स के एक संयोजन से हैशकोड प्राप्त करना आपको एक हैश फ़ंक्शन के साथ छोड़ देता है जो स्ट्रिंग्स के किसी भी संयोजन के लिए समान है जो एक ही स्ट्रिंग को जोड़ते हैं।
स्क्वायरकॉप

1
तो यह मेटा-चर्चा है और प्रश्न से संबंधित नहीं है? ;-)
हूपी

1
यह एक प्रस्तावित उत्तर के लिए एक सुधार है जिसमें काफी महत्वपूर्ण दोष है।
स्क्वायरकैप

यह एक बहुत ही सीमित कार्यान्वयन है
क्रिस्टोफर रुकिंस्की

आपका कार्यान्वयन समस्या से बचने और एक दूसरे का परिचय देता है; स्वैपिंग fooऔर barउसी की ओर जाता है hashCode। आपका toStringAFAIK संकलन नहीं करता है, और यदि ऐसा होता है, तो यह बहुत ही अयोग्य है। कुछ ऐसा है जो 109 * getFoo().hashCode() + 57 * getBar().hashCode()तेज, सरल है और कोई अनावश्यक टकराव पैदा नहीं करता है।
मातरिनस

2

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


2

Apache Commons EqualsBuilder और HashCodeBuilder पर प्रतिबिंब विधियों का उपयोग करें ।


1
यदि आप इसका उपयोग करने जा रहे हैं, तो ध्यान रखें कि प्रतिबिंब महंगा है। मैं ईमानदारी से कोड को दूर फेंक के अलावा किसी भी चीज के लिए इसका इस्तेमाल नहीं करूंगा।
जेम्स मैकमोहन

2

मैं एक छोटे रैपर का उपयोग करता हूं Arrays.deepHashCode(...)क्योंकि यह पैरामीटर के रूप में आपूर्ति की गई सरणियों को सही ढंग से संभालता है

public static int hash(final Object... objects) {
    return Arrays.deepHashCode(objects);
}

1

किसी भी हैशिंग विधि जो समान सीमा पर हैश मान को समान रूप से वितरित करती है एक अच्छा कार्यान्वयन है। प्रभावी जावा ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&&ig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=hi&hl=hi ) देखें। हैशकोड कार्यान्वयन के लिए वहाँ (आइटम 9 मुझे लगता है ...)।


1

मैं कक्षा के ऑब्जेक्ट से Google संग्रह कार्यवाहियों के उपयोगिता विधियों का उपयोग करना पसंद करता हूं जो मुझे अपना कोड साफ रखने में मदद करता है। आईडीई के खाके से बहुत बार equalsऔर hashcodeतरीके बनाए जाते हैं, इसलिए पढ़ने के लिए उनकी सफाई नहीं होती है।


1

यहाँ एक और JDK 1.7+ अप्रोच लॉजिक्स के साथ एप्रोच प्रदर्शन है। मैं इसे ऑब्जेक्ट क्लास हैशकोड () हिसाब, शुद्ध JDK निर्भरता और कोई अतिरिक्त मैनुअल काम के साथ बहुत आश्वस्त के रूप में देखता हूं। कृपया ध्यान दें Objects.hash()अशक्त सहिष्णु है।

मैंने किसी भी equals()कार्यान्वयन को शामिल नहीं किया है लेकिन वास्तव में आपको इसकी आवश्यकता होगी।

import java.util.Objects;

public class Demo {

    public static class A {

        private final String param1;

        public A(final String param1) {
            this.param1 = param1;
        }

        @Override
        public int hashCode() {
            return Objects.hash(
                super.hashCode(),
                this.param1);
        }

    }

    public static class B extends A {

        private final String param2;
        private final String param3;

        public B(
            final String param1,
            final String param2,
            final String param3) {

            super(param1);
            this.param2 = param2;
            this.param3 = param3;
        }

        @Override
        public final int hashCode() {
            return Objects.hash(
                super.hashCode(),
                this.param2,
                this.param3);
        }
    }

    public static void main(String [] args) {

        A a = new A("A");
        B b = new B("A", "B", "C");

        System.out.println("A: " + a.hashCode());
        System.out.println("B: " + b.hashCode());
    }

}

1

मानक कार्यान्वयन कमजोर है और इसका उपयोग करने से अनावश्यक टकराव होता है। एक कल्पना करो

class ListPair {
    List<Integer> first;
    List<Integer> second;

    ListPair(List<Integer> first, List<Integer> second) {
        this.first = first;
        this.second = second;
    }

    public int hashCode() {
        return Objects.hashCode(first, second);
    }

    ...
}

अभी,

new ListPair(List.of(a), List.of(b, c))

तथा

new ListPair(List.of(b), List.of(a, c))

के रूप में इस्तेमाल किया गुणक के रूप में एक ही है hashCode, यहाँ पुन: 31*(a+b) + cउपयोग किया List.hashCodeजाता है। जाहिर है, टकराव अपरिहार्य हैं, लेकिन अनावश्यक टकराव पैदा करना सिर्फ ... अनावश्यक है।

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

प्राइम का उपयोग करना व्यर्थ है क्योंकि प्रिंसेस का रिंग Z / (2 ** 32) में कोई अर्थ नहीं है।

इसलिए, मैं बेतरतीब ढंग से चुनी गई बड़ी विषम संख्या (प्राइम लेने के लिए स्वतंत्र महसूस) का उपयोग करने की सलाह दूंगा। जैसा कि i86 / amd64 CPU एक हस्ताक्षरित बाइट में ऑपरेंड फिटिंग के लिए एक छोटे निर्देश का उपयोग कर सकते हैं, 109 जैसे गुणक के लिए एक छोटा गति लाभ है। टकराव को कम करने के लिए, 0x58a54cf5 जैसा कुछ लें।

अलग-अलग स्थानों पर अलग-अलग मल्टीप्लायरों का उपयोग करना सहायक है, लेकिन शायद अतिरिक्त काम को सही ठहराने के लिए पर्याप्त नहीं है।


0

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

seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);

यह एक समान वितरण सुनिश्चित करने का एक अच्छा काम करता है। यह सूत्र कैसे काम करता है इसकी कुछ चर्चा के लिए, StackOverflow पोस्ट देखें: जादू संख्या को बढ़ावा देने में :: hash_combine

Http://burtleburtle.net/bob/hash/doobs.html : पर विभिन्न हैश कार्यों की एक अच्छी चर्चा है


1
यह जावा के बारे में एक सवाल है, न कि सी ++।
डैनो

-1

एक साधारण वर्ग के लिए अक्सर हैशकोड () को उन क्षेत्रों के आधार पर लागू करना सबसे आसान होता है जो समान (चेक) कार्यान्वयन द्वारा जाँचे जाते हैं।

public class Zam {
    private String foo;
    private String bar;
    private String somethingElse;

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        Zam otherObj = (Zam)obj;

        if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
            if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
                return true;
            }
        }

        return false;
    }

    public int hashCode() {
        return (getFoo() + getBar()).hashCode();
    }

    public String getFoo() {
        return foo;
    }

    public String getBar() {
        return bar;
    }
}

सबसे महत्वपूर्ण बात यह है कि हैशकोड () और समतुल्य () संगत रखना: यदि बराबर () दो वस्तुओं के लिए सही लौटाता है, तो हैशकोड () एक ही मूल्य वापस करना चाहिए। यदि बराबर () गलत है, तो हैशकोड () अलग-अलग मान वापस करना चाहिए।


1
जैसे स्क्वायरकॉग पहले ही देख चुका है। यदि हैशकोड दो तारों के संघात से एक बार उत्पन्न होता है तो टकराव के द्रव्यमान उत्पन्न करना बेहद आसान है ("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc"):। यह गंभीर दोष है। दोनों क्षेत्रों के लिए हैशकोड का मूल्यांकन करना बेहतर होगा और फिर उनमें से रैखिक संयोजन की गणना करें (अधिमानतः गुणांक के रूप में primes का उपयोग करके)।
Krzysztof Jabłoński

@ KrzysztofJabłoński राइट। इसके अलावा, स्वैपिंग fooऔर barएक अनावश्यक टकराव पैदा करता है, भी।
मातरिनस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.