2010-10-28 10 views
35

मैं वस्तुओं है कि एक long क्षेत्र जिसका मान विशिष्ट, मेरे पूरे सिस्टम भर में एक विशेष वस्तु को दिखाता है बहुत एक GUID तरह की एक श्रृंखला है। तुलनात्मक रूप से इस आईडी का उपयोग करने के लिए मैंने Object.equals() को ओवर्रिडन किया है, क्योंकि मैं इसे ऑब्जेक्ट की प्रतियों के साथ काम करना चाहता हूं। अब मैं Object.hashCode() भी ओवरराइड करने के लिए, मूल रूप से मेरी long कुछ int को वापसी मान मानचित्रण जिसका अर्थ है चाहता हूँ।हैशकोड() में int से int को कैसे मैप करना चाहिए?

अगर मैं सही ढंग hashCode के प्रयोजन समझ में आया, यह मुख्य रूप से हैश तालिकाओं में प्रयोग किया जाता है, तो एक समान वितरण वांछनीय होगा। इसका मतलब यह होगा कि id % 2^32 वापस लौटना पर्याप्त होगा। क्या यह सब कुछ है, या मुझे किसी और चीज के बारे में पता होना चाहिए?

+0

बीटीडब्ल्यू अगर आप केवल 32 बिट्स रखना चाहते हैं, तो मॉड्यूलो ऑपरेशन की कोई आवश्यकता नहीं है। 'Int' पर कास्टिंग पर्याप्त है: 'int हैशकोड = (int) id'। – Grodriguez

+0

@Grodriguez क्षमा करें, लेकिन यह जवाब भयानक है! इससे कई ऑब्जेक्ट्स एक ही हैशकोड हो जाएंगे जो हैश टकराव के सभी प्रकार बनाएगा। आप हमेशा समान रूप से वितरित हैश कोड चाहते हैं। इसके अलावा स्वीकृत उत्तर सबसे अच्छा समाधान नहीं है, क्योंकि जावा 8 ने एक बेहतर समाधान पेश किया था। कृपया "नाथन" द्वारा दिए गए उत्तर का संदर्भ लें, क्योंकि 'Long.hashcode (long)' स्टैक –

+1

@Neuron पर कोई नई ऑब्जेक्ट नहीं बनाता है कोई भी हैश फ़ंक्शन जो 32 बिट एक में 64 बिट मान को मानचित्र करता है "कारण कई ऑब्जेक्ट एक ही हैशकोड "। इससे बचने के लिए कोई रास्ता नहीं है। इसके अलावा, इस बात की कोई गारंटी नहीं है कि '(this.longValue()^(this.longValue() >>> 32))' मूल्य के निचले 32 बिट्स को रखने के बजाय 'समान रूप से वितरित हैश कोड उत्पन्न करता है। – Grodriguez

उत्तर

66

जावा 8 जब से तुम

Long.hashCode(guid); 

उपयोग कर सकते हैं जावा के पुराने संस्करणों के लिए आप उपयोग कर सकते हैं निम्नलिखित:

Long.valueOf(guid).hashCode(); 

ध्यान दें कि यह समाधान ढेर के लिए एक नई वस्तु बनाता है, जबकि पहले नहीं (हालांकि यह संभावना है कि जावा ऑब्जेक्ट सृजन को दूर करता है ..)

दस्तावेज़ों को देखते हुए, दोनों तरीकों से केवल निम्नलिखित एल्गोरिदम का उपयोग करें:

(int)(this.longValue()^(this.longValue()>>>32)) 

ये सभ्य समाधान हैं क्योंकि वे जावा लाइब्रेरी का उपयोग करते हैं - पहले से परीक्षण किए गए किसी भी चीज़ का लाभ उठाने के लिए हमेशा बेहतर होता है।

+2

यह महंगा हो सकता है क्योंकि इसे ऑब्जेक्ट सृजन की आवश्यकता होती है (इसलिए गुवा वैकल्पिक)। एल्गोरिदम के लिए ही, खतरनाक होने का एकमात्र समय तब होता है जब ऊपरी और निचले 32 बिट्स का अर्थ सहसंबंध होता है। उदाहरण के लिए, यह एक 'प्वाइंट' वर्ग के लिए एक भयानक हैशकोड होगा जो 32-बिट x और y समन्वय को एक लंबे समय तक संग्रहीत करता है। –

+1

ऑब्जेक्ट निर्माण को अनुकूलित करने के लिए वीएम के लिए यह पूरी तरह से संभव है। ऐसा नहीं है कि मैं उस पर भरोसा करना चाहता हूं। – TofuBeer

+0

@ मार्क वास्तव में यह पॉइंट क्लास के लिए भी ठीक काम करेगा। असल में, अगर मेरे पास अलग एक्स, वाई इन्ट्स के साथ पॉइंट क्लास था, तो मैं हैशकोड को इसी तरह के फैशन (x^y) में उत्पन्न करूंगा। – james

5

आप सही ढंग से hashCode के प्रयोजन समझ लिया है। हां, एक समान वितरण वांछनीय है (हालांकि वास्तविक आवश्यकता नहीं है)।

मैं ((id >> 32)^id) सुझाव है।

ऊपर अभिव्यक्ति:

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

+1। यह 'java.lang.Long' के लिए परिभाषित हैशकोड लगभग है, हालांकि यह' >> 'के बजाय '>>>' का उपयोग करता है। मुझे आश्चर्य है कि '(नया लांग (आईडी)) हैशकोड(); ', या इसी तरह, अनुकूलित किया जाएगा। –

+3

@ स्टेव: इस मामले में '>>>' और '>> 'के बीच कोई अंतर नहीं है, क्योंकि शिफ्ट के दौरान पेश की गई अतिरिक्त 32 बिट्स को वैसे भी त्याग दिया जाएगा। – Grodriguez

1
int result = (int)((longVal >> 32)^longVal); 

अधिक अच्छी तरह से, वितरित किया जाएगा क्योंकि सापेक्ष विभिन्न मूल्य वापस नहीं देगा, यदि आपका लंबे मूल्य का केवल ऊपरी बिट्स बदल गया है।

9

यह एक छोटी सी बात का एक सा है, तो आप पहले से ही Guava उपयोग कर रहा है नहीं कर रहे हैं, लेकिन अमरूद do this for you अच्छी तरह से कर सकते हैं:

public int hashCode() { 
    return Longs.hashCode(id); 
} 

है कि आप Long.valueOf(id).hashCode() के बराबर देता है:

return (int) (value^(value >>> 32)); 

साथ ही, यदि आपके पास हैशकोड का हिस्सा अन्य मूल्य या ऑब्जेक्ट्स थे, तो आप केवल

लिख सकते थे

longLong में ऑटोबॉक्साइड किया जाएगा ताकि आपको संपूर्ण हैशकोड के हिस्से के रूप में इसके लिए सही हैशकोड प्राप्त हो सके।

+0

मैं इसके लिए सिर्फ एक पूरी नई लाइब्रेरी नहीं खींचूंगा, लेकिन मैंने कभी भी अमरूद के बारे में नहीं सुना था, यह अधिक सामान्य दृष्टिकोण से देखने में काफी मददगार और लायक लगता है। धन्यवाद! –

+1

@ हनो: हाँ, यह निश्चित रूप से इस छोटी सी चीज़ के लिए इसके लायक नहीं होगा। लेकिन यह बहुत उपयोगी सुविधाओं के साथ एक महान पुस्तकालय है! – ColinD

+0

मैं पिछले कुछ सालों से ज्यादा जावा नहीं कर रहा हूं, लेकिन अमरूद अद्भुत है और आपके कोड को बेहतर बनाने के लिए बहुत उपयोगी वर्ग प्रदान करता है। –

2

(l >> 32)^l ज्यादातर मामलों में एक अच्छा हैशकोड है; खासकर जब लंबे समय तक एक समान वितरण होता है।

चूंकि यह स्वीकार्य उत्तर था, इसलिए मैं अपनी कुछ टिप्पणियों को स्पष्ट करने के लिए इसे पोस्ट कर रहा हूं कि यह लंबे समय तक एक अच्छा हैशकोड नहीं है।

public class Point { 
    private final long coords; //x in high-bits, y in low 
    public int getX() { 
     return (int)(coords >> 32); 
    } 
    public int getY() { 
     return (int)coords; 
    } 
    public int hashCode() { 
     return (int)((coords >> 32)^(coords)); 
    } 
} 

यह काल्पनिक लग सकता है, लेकिन कभी कभी आप एक लंबे में पैक एकाधिक "फील्ड" है:

उदाहरण मैं दे दी है इस तरह एक प्वाइंट वर्ग था।

तो coords फ़ील्ड x के 32 बिट्स और 32 बिट्स वाई का प्रतिनिधित्व करता है। तो यह एक समस्या क्यों है? खैर, ऐसा नहीं है कि प्रत्येक एक्स और वाई को उनके संबंधित 32 बिट्स पर समान रूप से वितरित किया जाता है। लेकिन यह अभ्यास में असंभव है। अधिक संभावना है कि एक्स और वाई कुछ संख्या से बंधे हैं। आइए 1024 कहें क्योंकि यह 2^10 है। इसका मतलब है कि अधिक से अधिक प्रत्येक एक्स और वाई के निचले 10 बिट सेट कर रहे हैं:

00000000 00000000 000000XX XXXXXXXX 00000000 00000000 000000YY YYYYYYYY 

2^20 (1024 * 1024) संभव संयोजनों रहे हैं। लेकिन ऑपरेशन हैशकोड क्या कर रहा है?

00000000 00000000 000000XX XXXXXXXX 
^ 00000000 00000000 000000YY YYYYYYYY 
------------------------------------- 
= 00000000 00000000 000000?? ???????? 

पर सबसे 2^10 (1024) संभव hashCode मूल्यों के बाद से ही कम 10 बिट कभी शून्य के अलावा कुछ भी हो सकता हैं। हैश मानों का वास्तविक मूल्यों का अनुपात 1024:(1024*1024) या 1:1024 है। तो बल्ले से बाहर एक 1/1024 संभावना है कि दो संख्याओं में एक ही हैश है।

अब birthday problem से गणित लागू करके टकराव की संभावना की गणना करें। पी (एन) को संभावना है कि एन मानों के साथ कम से कम एक टकराव होगा। हम जानते हैं कि पी (1025+) = 1 क्योंकि केवल 1024 मान हैं।

p(n) = 1 - (n! * (1024 choose n))/1024^n 

यह निम्न करने के लिए बाहर काम करता है:

n: p(n) 
1: 0.00000 
2: 0.00098 
3: 0.00293 
4: 0.00585 
5: 0.00973 
6: 0.01457 
... 
38: 0.50096 
... 
79: 0.95444 
... 
148: 0.99999 
सिर्फ 38 आइटम के साथ

, वहाँ शायद एक टक्कर है। 148 वस्तुओं के साथ, 99.9 99% मौका (कम से कम एक) टक्कर है। 148 वस्तुओं के साथ, प्रत्येक आइटम में किसी अन्य आइटम के साथ टकराने का 7% मौका होता है। उचित हैशिंग फ़ंक्शन के साथ, डोमेन का ज्ञान लेते हुए, ये संख्या आसानी से 0

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

+0

आखिरकार, यह उत्तर मेरे मूल कथन के लिए ऑर्थोगोनल है कि पॉइंट क्लास के लिए x^y का उपयोग करना एक उचित हैश है। यहां आपका तर्क यह है कि यह उचित नहीं है + यदि + x और y अधिकतम 1024 तक सीमित हैं। वैध बिंदु, लेकिन मेरे मूल कथन का खंडन नहीं करता है। – james

+0

@james: हालांकि यह सिर्फ अनावश्यक रूप से अज्ञानी है, मेरा मुद्दा है। अभ्यास में कितनी बार अंक अपने डोमेन पर समान रूप से वितरित अंकों का एक सेट है? लगभग नहीं। ब्लॉच इस प्रकार के नुस्खा हैशकोड के लिए सुझाव देता है: 'कुछ प्राइम * getX() + getY() '। यह बहुत अच्छा नहीं है, लेकिन डोमेन के बारे में कुछ भी जानने के बिना डेटा "असंबद्ध" करने का प्रयास करने के लिए प्रमुख है। यह भी है कि असली 'प्वाइंट 2 डी' कक्षा सामान्य रूप से कैसे काम करती है। –

+0

@james: वैसे, यह x और y के लिए 2^30 से भी जुड़ा हुआ है, हालांकि 2^30 के लिए आप टकराव के टन की अपेक्षा करेंगे; इसके बारे में आप कुछ भी नहीं कर सकते हैं। 1024 को बस चुना गया था क्योंकि इसे समझाना आसान है। –

3

जावा 8 जेडीके में Long.hashCode(long) जोड़ता है।

निम्नलिखित कोड उच्च प्रदर्शन प्राप्त कर सकता है। यह कोड 64-बिट long के साथ कंप्यूटिंग के बजाय 32-बिट int की गणना को कम कर देता है। इससे 32-बिट और छोटे आर्किटेक्चर पर अंतर हो सकता है। X86 मशीनों पर 32-बिट प्रक्रियाओं को इसे एक ही निर्देश में अनुकूलित किया जा सकता है जो बस XORs 2 रजिस्टर्स है।

return (int)(value^(value >>> 32));

अन्य उत्तर में बताया गया है, इस नहीं एक अच्छा avalanche effect और इसलिए है टकराव का कारण बन सकता है। उच्च हिमस्खलन प्रभाव सुनिश्चित करने के लिए क्रिप्टोग्राफिक हैश फ़ंक्शन के साथ जा सकता है। हालांकि, अन्य एल्गोरिदम जैसे Murmur Hash (अधिक information) हैं जिनके पास बहुत अच्छा हिमस्खलन प्रभाव है लेकिन अधिक CPU समय का उपभोग नहीं करते हैं।