जावा स्ट्रिंग पर हैशकोड () की संगति


134

जावा स्ट्रिंग का हैशकोड मान (String.hashCode () ) के रूप में गणना की जाती है :

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

क्या कोई परिस्थितियां हैं (जेवीएम संस्करण, विक्रेता, आदि का कहना है) जिसके तहत निम्नलिखित अभिव्यक्ति झूठी का मूल्यांकन करेगी?

boolean expression = "This is a Java string".hashCode() == 586653468

अद्यतन # 1: यदि आप दावा करते हैं कि इसका उत्तर "हाँ, ऐसी परिस्थितियाँ हैं" - तो कृपया "यह जावा स्ट्रिंग है" का ठोस उदाहरण दें। .शशकोड ()! = 586653468। विशिष्ट या ठोस होने का प्रयास करें। यथासंभव।

अपडेट # 2: हम सभी जानते हैं कि हैशकोड () के कार्यान्वयन विवरणों पर भरोसा करना सामान्य तौर पर बुरा है। हालाँकि, मैं विशेष रूप से String.hashCode () के बारे में बात कर रहा हूँ - इसलिए कृपया उत्तर String.hashCode () पर केंद्रित रखें। Object.hashCode () इस प्रश्न के संदर्भ में पूरी तरह अप्रासंगिक है।


2
क्या आपको वास्तव में इस कार्यक्षमता की आवश्यकता है? आपको सटीक मान की आवश्यकता क्यों है?
ब्रायन एग्न्यू

26
@ ब्रायन: मैं String.hashCode () के अनुबंध को समझने की कोशिश कर रहा हूं।
नोरव

3
@Knorv यह समझने की जरूरत नहीं है कि यह कैसे काम करता है - अनुबंध और इसके उल्टे अर्थ को समझने के लिए यह अधिक महत्वपूर्ण है।
एम.पी.

45
@ एमपीपी: आपके इनपुट के लिए धन्यवाद, लेकिन मुझे लगता है कि यह मुझे तय करना है।
नोरव

उन्होंने पहले चरित्र को सबसे बड़ी शक्ति क्यों दिया? जब आप अतिरिक्त गणना को संरक्षित करने के लिए गति के लिए इसे अनुकूलित करना चाहते हैं, तो आप पिछले एक की शक्ति को संग्रहीत करेंगे, फिर भी पिछले एक पिछले चरित्र से पहले एक तक होगा। इसका मतलब यह है कि कैश मिस भी होगा। एल्गोरिथ्म का होना अधिक कुशल नहीं है: s [0] + s [1] * 31 + s [2] * 31 ^ 2 + ... + s [n-1] * 31 ^ [n-1 ]?
एंड्रॉयड डेवलपर

जवाबों:


101

मैं उस दस्तावेज को जावा 1.2 के रूप में वापस देख सकता हूं।

हालांकि यह सच है कि सामान्य तौर पर आप एक हैश कोड कार्यान्वयन पर निर्भर नहीं रह सकते हैं, यह अब के लिए व्यवहार का दस्तावेज है java.lang.String, इसलिए इसे बदलना मौजूदा अनुबंधों को तोड़ने के रूप में गिना जाएगा।

जहाँ भी संभव हो, आपको संस्करणों आदि में समान रहने वाले हैश कोड पर भरोसा नहीं करना चाहिए - लेकिन मेरे दिमाग java.lang.Stringमें एक विशेष मामला है क्योंकि एल्गोरिथ्म निर्दिष्ट किया गया है ... इसलिए जब तक आप रिलीज से पहले संगतता को छोड़ने के लिए तैयार हैं निश्चित रूप से एल्गोरिथ्म निर्दिष्ट किया गया था।


7
स्ट्रिंग के प्रलेखित व्यवहार को जावा 1.2 से एपीआई के v1.1 में निर्दिष्ट किया गया है, स्ट्रिंग कोड के लिए हैश कोड गणना निर्दिष्ट नहीं है।
मार्टिन ओकोनर

इस मामले में हम बेहतर तरीके से अपने स्वयं के हैशिंग कोड्स लिखेंगे?
फेल्पे

@ फ़ेल्प: मैं वास्तव में नहीं जानता कि आप यहाँ क्या कहना चाह रहे हैं, मुझे डर है।
जॉन स्कीट

@JonSkeet मेरा मतलब है, इस मामले में हम शायद पोर्टेबिलिटी देने के लिए अपना खुद का हैश बनाने के लिए अपना कोड लिख सकते हैं। क्या यह?
फेल्प

@ फ़ेल्प: यह बिल्कुल स्पष्ट नहीं है कि आप किस तरह की पोर्टेबिलिटी के बारे में बात कर रहे हैं, और न ही वास्तव में "इस मामले में" से आपका क्या मतलब है - किस विशिष्ट परिदृश्य में? मुझे संदेह है कि आपको एक नया प्रश्न पूछना चाहिए।
जॉन स्कीट

18

मैंने JDK 1.0 और 1.1 और> = 1.2 के बारे में कुछ पाया:

JDK 1.0.x और 1.1.x में लंबे स्ट्रिंग्स के लिए हैशकोड फ़ंक्शन हर nth चरित्र का नमूना लेकर काम करता है। यह बहुत अच्छी तरह से गारंटी देता है कि आपके पास एक ही मूल्य के कई स्ट्रिंग्स हैशिंग होंगे, इस प्रकार हैशटेबल लुकअप को धीमा कर देगा। JDK 1.2 में फ़ंक्शन को 31 से अब तक परिणाम गुणा करने के लिए बेहतर बनाया गया है और फिर अगले चरित्र को अनुक्रम में जोड़ें। यह थोड़ा धीमा है, लेकिन टकराव से बचने में बहुत बेहतर है। स्रोत: http://mindprod.com/jgloss/hashcode.html

कुछ अलग है, क्योंकि आपको एक नंबर की आवश्यकता है: हैशकोड के बजाय CRC32 या MD5 का उपयोग कैसे करें और आप जाने के लिए अच्छा है - कोई चर्चा नहीं और कोई चिंता नहीं ...


8

आपको किसी विशिष्ट मान के बराबर हैश कोड पर निर्भर नहीं होना चाहिए। बस यह एक ही निष्पादन के भीतर लगातार परिणाम लौटाएगा। एपीआई डॉक्स निम्नलिखित कहते हैं:

हैशकोड का सामान्य अनुबंध है:

  • जब भी जावा एप्लिकेशन के निष्पादन के दौरान इसे एक से अधिक बार एक ही ऑब्जेक्ट पर लागू किया जाता है, तो हैशकोड विधि को लगातार एक ही पूर्णांक को वापस करना होगा, बशर्ते वस्तु पर तुलना में उपयोग की गई कोई भी जानकारी संशोधित न हो। इस पूर्णांक को एक अनुप्रयोग के एक निष्पादन से एक ही अनुप्रयोग के दूसरे निष्पादन के अनुरूप नहीं रहना चाहिए।

EDIT चूंकि String.hashCode () के लिए javadoc निर्दिष्ट करता है कि स्ट्रिंग के हैश कोड की गणना कैसे की जाती है, इसका कोई भी उल्लंघन सार्वजनिक API विनिर्देश का उल्लंघन होगा।


1
आपका उत्तर मान्य है, लेकिन पूछे गए विशिष्ट प्रश्न को संबोधित नहीं करता है।
नोरव

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

4

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

ध्यान दें कि यह सैद्धांतिक नहीं है। Java.lang.String के लिए हैश फ़ंक्शन को JDK1.2 में बदल दिया गया था (पुराने हैश को URL या फ़ाइल नामों की तरह पदानुक्रमित स्ट्रिंग्स के साथ समस्या थी, क्योंकि यह स्ट्रिंग्स के लिए उसी हैश का उत्पादन करने के लिए गया था जो अंत में भिन्न था)।

java.lang.String एक विशेष मामला है, जैसा कि इसके हैशकोड () का एल्गोरिथ्म (अब) प्रलेखित है, इसलिए आप शायद उस पर भरोसा कर सकते हैं। मैं अभी भी इसे बुरा अभ्यास मानता हूँ। यदि आपको विशेष, प्रलेखित गुणों के साथ हैश एल्गोरिथ्म की आवश्यकता है, तो बस एक :-) लिखें।


4
लेकिन क्या JDK 1.2 से पहले डॉक्स में एल्गोरिथ्म निर्दिष्ट था? यदि नहीं, तो यह एक अलग स्थिति है। एल्गोरिथ्म को अब डॉक्स में रखा गया है, इसलिए इसे बदलना एक सार्वजनिक अनुबंध में एक ब्रेकिंग परिवर्तन होगा।
जॉन स्कीट

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

@Jon Skeet: आह, यह नहीं पता था कि String.hashCode () का एल्गोरिथ्म प्रलेखित है। ज़ाहिर है कि चीज़ें बदलती हैं। मेरी टिप्पणी नवीनीकृत।
साल्सेक

3

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

नीचे पंक्ति है, मैं के कार्यान्वयन पर भरोसा नहीं करेगा hashCode()

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


1
आपके उत्तर के लिए धन्यवाद। जब आप "यह एक जावा स्ट्रिंग है" .hashCode ()! = 586653468 का ठोस उदाहरण दे सकते हैं?
नोरव

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

2
"भविष्य के जावा संस्करण में अपग्रेड करने से समस्या हो सकती है"। भविष्य के जावा संस्करण में अपग्रेड पूरी तरह से हैशकोड विधि को हटा सकता है। या इसे हमेशा स्ट्रिंग के लिए 0 वापस करें। यह हां के लिए असंगत परिवर्तन है। सवाल यह है कि क्या Sun ^ HOracle ^ HThe JCP इसे ब्रेकिंग चेंज मानते हैं और इसलिए इससे बचना चाहिए। चूंकि एल्गोरिथ्म अनुबंध में है, इसलिए एक आशा है कि वे करेंगे।
स्टीव जेसोप

@SteveJessop अच्छी तरह से, क्योंकि switchस्ट्रिंग्स पर बयान एक विशेष निश्चित हैश कोड पर निर्भर कोड को संकलित करते हैं, तो Stringहैश कोड एल्गोरिथ्म में परिवर्तन निश्चित रूप से मौजूदा कोड को तोड़ देगा ...
Holger

3

बस अपने प्रश्न का उत्तर देने के लिए और किसी भी चर्चा को जारी रखने के लिए नहीं। अपाचे हार्मनी JDK कार्यान्वयन एक अलग एल्गोरिथ्म का उपयोग करता है, कम से कम यह पूरी तरह से अलग दिखता है:

सन JDK

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

अपाचे हारमोनी

public int hashCode() {
    if (hashCode == 0) {
        int hash = 0, multiplier = 1;
        for (int i = offset + count - 1; i >= offset; i--) {
            hash += value[i] * multiplier;
            int shifted = multiplier << 5;
            multiplier = shifted - multiplier;
        }
        hashCode = hash;
    }
    return hashCode;
}

बेझिझक इसकी जांच करें ...


23
मुझे लगता है कि वे सिर्फ शांत हो रहे हैं और इसे अनुकूलित कर रहे हैं। :) "(गुणक << 5) - गुणक" सिर्फ 31 * गुणक, सब के बाद ... है
खोलना

ठीक है, यह जाँचने के लिए बहुत आलसी था। धन्यवाद!
रेनेस

1
लेकिन मेरी तरफ से यह स्पष्ट करने के लिए ... कभी भी हैशकोड पर भरोसा न करें क्योंकि हैशकोड कुछ आंतरिक है।
रेनेस

1
"ऑफसेट", "गणना" और "हैशकोड" के चर क्या हैं? मुझे लगता है कि "हैशकोड" का उपयोग कैशेड वैल्यू के रूप में किया जाता है, भविष्य की गणना से बचने के लिए, और यह कि "गिनती" वर्णों की संख्या है, लेकिन "ऑफसेट" क्या है? मान लीजिए कि मैं इस कोड का उपयोग करना चाहता हूं, ताकि यह लगातार हो, एक तार दिया जाए, मुझे इसे क्या करना चाहिए?
Android डेवलपर

1
@androiddeveloper अब यह एक दिलचस्प सवाल है - हालांकि मुझे आपके उपयोगकर्ता नाम के आधार पर इसका अनुमान लगाना चाहिए था। से एंड्रॉयड डॉक्स यह अनुबंध की तरह दिखता है एक ही है: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]जब तक मैं गलत कर रहा हूँ, इस वजह से एंड्रॉयड बिना किसी परिवर्तन के स्ट्रिंग वस्तु की सूर्य की कार्यान्वयन उपयोग करता है।
कार्तिक चुघ

2

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


मैं यह कहने जा रहा था। जबकि अन्य उत्तर प्रश्न का उत्तर देते हैं, एक अलग हैशकोड फ़ंक्शन लिखना शायद knorv की समस्या का उचित समाधान है।
निक

1

स्ट्रिंग में वर्णों के ASCII मूल्यों के आधार पर हैशकोड की गणना की जाएगी।

यह स्ट्रिंग क्लास में कार्यान्वयन इस प्रकार है

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        hash = h = isLatin1() ? StringLatin1.hashCode(value)
                              : StringUTF16.hashCode(value);
    }
    return h;
}

हैशकोड में टकराव अपरिहार्य हैं। उदाहरण के लिए, तार "ईए" और "एफबी" 2236 के समान हैशकोड देते हैं

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