ConcurrentHashMap अशक्त कुंजियों और मूल्यों को क्यों रोकता है?


141

JavaDoc ConcurrentHashMapयह कहता है:

जैसा Hashtableलेकिन विपरीत HashMap, इस वर्ग करता है नहीं की अनुमति देने के nullएक कुंजी या मूल्य के रूप में प्रयोग की जाने वाली।

मेरा सवाल: क्यों?

दूसरा सवाल: क्यों Hashtableअशक्त नहीं होने देता?

मैंने डेटा संग्रहीत करने के लिए बहुत सारे HashMaps का उपयोग किया है। लेकिन जब ConcurrentHashMapमैं बदल रहा हूं तो NullPointerException की वजह से कई बार परेशानी हुई।


1
मुझे लगता है कि यह एक बहुत कष्टप्रद असंगति है। EnumMap नल या तो अनुमति नहीं है। स्पष्ट रूप से कोई तकनीकी सीमा नहीं है जो अशक्त चाबियों को अस्वीकार करती है। मानचित्र <K, V> के लिए, बस एक वी-टाइप फ़ील्ड शून्य कुंजियों के लिए समर्थन प्रदान करेगा (शायद एक और बुलियन फ़ील्ड यदि आप अशक्त मूल्य और कोई मूल्य के बीच अंतर करना चाहते हैं)।
रे

6
एक बेहतर सवाल यह है कि "हाशप एक अशक्त कुंजी और अशक्त मूल्यों की अनुमति क्यों देता है?"। या संभवतः, "जावा सभी प्रकार के निवासियों को शून्य करने की अनुमति क्यों देता है?", या यहां तक ​​कि "जावा में सभी शून्य क्यों हैं?"।
जेड वेस्ले-स्मिथ

जवाबों:


220

ConcurrentHashMapखुद के लेखक से (डौग ली) :

ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) में नल की अनुमति नहीं होने का मुख्य कारण यह है कि अस्पष्टताएं जो गैर-समवर्ती नक्शे में केवल मुश्किल से सहनीय हो सकती हैं, उन्हें समायोजित नहीं किया जा सकता है। मुख्य यह है कि यदि map.get(key)रिटर्न मिलता है null, तो आप यह पता नहीं लगा सकते हैं कि कुंजी को मैप करने के nullलिए कुंजी को मैप किया गया है या नहीं। एक गैर-समवर्ती मानचित्र में, आप इसके माध्यम से जांच कर सकते हैं map.contains(key), लेकिन समवर्ती एक में, नक्शे कॉल के बीच बदल सकते हैं।


7
धन्यवाद, लेकिन कुंजी के रूप में अशक्त होने के बारे में क्या?
अमितडब्ल्यू

2
Optionalआंतरिक रूप से मानों का उपयोग क्यों नहीं किया जाता
benez

2
@benez Optionalएक जावा 8 फीचर है, जो तब (जावा 5) में उपलब्ध नहीं था। आप Optionalवास्तव में अब एस का उपयोग कर सकते हैं ।
ब्रूनो

@AmitW, मुझे लगता है कि ans एक ही है यानी अस्पष्टता। उदाहरण के लिए मान लीजिए कि एक धागा नल के रूप में एक कुंजी बनाता है और इसके खिलाफ एक मूल्य रखता है। फिर एक और धागे ने एक और कुंजी को शून्य में बदल दिया। जब दूसरा थ्रेड नया मान जोड़ने का प्रयास करता है, तो इसे बदल दिया जाएगा। यदि दूसरा धागा मूल्य प्राप्त करने की कोशिश करता है, तो इसे एक अलग कुंजी के लिए मान मिलेगा, पहले एक द्वारा संशोधित। ऐसी स्थिति से बचना चाहिए।
Dexter

44

मेरा मानना है कि यह है, कम से कम आंशिक रूप से, आप गठबंधन करने के लिए अनुमति देने के लिए containsKeyऔर getएक कॉल में। यदि मानचित्र getअशक्त पकड़ सकता है, तो यह बताने का कोई तरीका नहीं है कि क्या कोई अशक्त लौट रहा है क्योंकि उस मूल्य के लिए कोई कुंजी नहीं थी, या केवल इसलिए कि मान शून्य था।

वह एक समस्या क्यों है? क्योंकि अपने आप को करने के लिए कोई सुरक्षित तरीका नहीं है। निम्नलिखित कोड लें:

if (m.containsKey(k)) {
   return m.get(k);
} else {
   throw new KeyNotPresentException();
}

चूंकि mएक समवर्ती नक्शा है, कुंजी k को कॉल containsKeyऔर getकॉल के बीच हटाया जा सकता है , जिससे इस स्निपेट को एक नल वापस करना होगा जो वांछित के बजाय तालिका में कभी नहीं था KeyNotPresentException

आम तौर पर आप इसे सिंक्रनाइज़ करके हल करेंगे, लेकिन समवर्ती नक्शे के साथ निश्चित रूप से काम नहीं करेंगे। इसलिए हस्ताक्षर के लिए getबदलाव करना पड़ा, और पीछे की ओर से संगत तरीके से ऐसा करने का एकमात्र तरीका उपयोगकर्ता को पहले स्थान पर शून्य मान डालने से रोकना था, और "कुंजी नहीं मिली" के लिए प्लेसहोल्डर के रूप में उपयोग करना जारी रखें।


आप कर सकते हैं map.getOrDefault(key, NULL_MARKER)। यदि यह है null, तो मान था null। यदि यह वापस आता है NULL_MARKER, तो मान मौजूद नहीं था।
ओलिव

@ केवल जावा 8 के रूप में। इसके अलावा, उस प्रकार के लिए एक समझदार शून्य मार्कर नहीं हो सकता है।
ऐलिस परसेल

@AlicePurcell, "लेकिन एक समवर्ती नक्शे के साथ जो निश्चित रूप से काम नहीं करेगा" - क्यों, मैं समान रूप से समवर्ती संस्करण पर सिंक्रनाइज़ कर सकता हूं - इसलिए सोच रहा हूं कि यह काम क्यों नहीं करेगा। क्या आप इसे विस्तृत कर सकते हैं।
samshers

@samshers एक समवर्ती नक्शे पर कोई भी ऑपरेशन सिंक्रनाइज़ नहीं होता है, इसलिए आपको बाह्य रूप से सभी कॉल को सिंक्रनाइज़ करने की आवश्यकता होगी, जिस बिंदु पर आपने केवल समवर्ती मानचित्र होने के सभी प्रदर्शन लाभ नहीं खोए हैं, आपने भविष्य के रखवाले के लिए एक जाल छोड़ दिया है स्वाभाविक रूप से उम्मीद होगी कि बिना समकालन के समवर्ती नक्शे का सुरक्षित रूप से उपयोग करने में सक्षम हो।
ऐलिस परसेल

@AlicePurcell, महान। यद्यपि तकनीकी रूप से संभव है, कि निश्चित रूप से एक रखरखाव दुःस्वप्न होने जा रहा है और यह किसी भी बाद के उपयोगकर्ताओं द्वारा अपेक्षित नहीं है कि उन्हें समवर्ती संस्करण पर सिंक करना है।
संस्कार

4

जोश बलोच ने डिजाइन किया HashMap; डग ले डिजाइन ConcurrentHashMap। मुझे आशा है कि यह परिवाद नहीं है। वास्तव में मुझे लगता है कि समस्या यह है कि नल को अक्सर लपेटने की आवश्यकता होती है ताकि असली अशक्त अनैतिक रूप से खड़ा हो सके। यदि क्लाइंट कोड को नल की आवश्यकता होती है, तो वह नल को स्वयं लपेटने की ((छोटे रूप में) लागत का भुगतान कर सकता है।


2

आप एक नल पर सिंक्रनाइज़ नहीं कर सकते।

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

उस स्थिति में, मुझे संदेह है कि उन्होंने हैशटेबल की नकल करने के लिए ऐसा किया था, और मुझे संदेह है कि हैशटेबल ने ऐसा इसलिए किया क्योंकि संबंधपरक डेटाबेस की दुनिया में, null! = Null, इसलिए एक कुंजी के रूप में नल का उपयोग करने का कोई अर्थ नहीं है।


है ना? मानचित्र की कुंजियों और मूल्यों पर कोई सिंक्रनाइज़ेशन नहीं किया गया है। इसका कोई मतलब नहीं होगा।
टोबियास मुलर

लॉकिंग के अन्य प्रकार हैं। वही इसे "समवर्ती" बनाता है। ऐसा करने के लिए, इसे लटकाने के लिए एक ऑब्जेक्ट की आवश्यकता होती है।
पॉल टॉम्बलिन

2
आंतरिक रूप से एक विशिष्ट वस्तु क्यों नहीं है जिसका उपयोग शून्य मानों को सिंक्रनाइज़ करने के लिए किया जा सकता है? जैसे "निजी वस्तु NULL = नई वस्तु ();" मुझे लगता है कि मैंने इसे पहले देखा है ...
मार्सेल

अन्य प्रकार के लॉकिंग का क्या मतलब है?
टोबियास म्यूलर

दरअसल, अब जब मैं स्रोत कोड gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/ पर देखता हूं तो मुझे उस पर गंभीर संदेह हो रहा है। ऐसा प्रतीत होता है कि यह खंड लॉकिंग का उपयोग करता है, व्यक्तिगत वस्तुओं पर लॉक नहीं करता है।
पॉल टॉम्बलिन

0

ConcurrentHashMap थ्रेड-सुरक्षित है। मेरा मानना ​​है कि अशक्त कुंजियों और मूल्यों को अनुमति नहीं देना यह सुनिश्चित करने का एक हिस्सा था कि यह धागा-सुरक्षित है।


0

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

वे शायद ConcurrentHashMapपूरी तरह से संगत / विनिमेय बनाना चाहते थे Hashtable। और के रूप में Hashtableशून्य कुंजी और मूल्यों की अनुमति नहीं देता है।


2
और हैशटेबल अशक्त का समर्थन क्यों नहीं करता है?
मार्सेल

इसके कोड को देखने से, मुझे एक स्पष्ट कारण नहीं दिखता है कि हैशटेबल अशक्त मूल्यों की अनुमति क्यों नहीं देता है। हो सकता है कि जब क्लास बनाया गया था तो यह सिर्फ एक एपीआई निर्णय था! HashMap ने आंतरिक रूप से नल-केस के लिए कुछ विशेष हैंडलिंग की है जो Hashtable नहीं करता है। (यह हमेशा NullPointerException को फेंकता है।)
टोबियास मुलर

-2

मुझे नहीं लगता कि अशक्त मूल्य को समाप्त करना एक सही विकल्प है। कई मामलों में, हम चाहते हैं कि मुख्य-वर्तमान नक्शे में शून्य मान के साथ एक कुंजी रखी जाए। हालाँकि, ConcurrentHashMap का उपयोग करके, हम ऐसा नहीं कर सकते। मेरा सुझाव है कि जेडीके का आने वाला संस्करण इसका समर्थन कर सकता है।


1
क्या आपने जवाचम्पियन के लिए प्रतिस्पर्धा के बारे में सोचा है?
ब्लैकबिशप

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