परिचय: HashMap — दोस्त या दुश्मन?
\n\nहर Java डेवलपर को देर-सबेर HashMap का सामना करना पड़ता है। यह शायद Java में Map इंटरफ़ेस का सबसे लोकप्रिय कार्यान्वयन है। लेकिन यहाँ एक विरोधाभास है: हर कोई जानता है कि मैप में एलिमेंट कैसे डाला जाए, लेकिन जब उसे ट्रैवर्स (इटरेट) करने की बात आती है, तो असली अफरा-तफरी शुरू हो जाती है। Stack Overflow पर "Iterate through a HashMap" प्रश्न ने हज़ारों बार देखा गया है, और वास्तविक प्रोजेक्ट्स में गलतियों की संख्या बहुत अधिक है।
इस लेख में हम Java में HashMap को ट्रैवर्स करते समय होने वाली 5 सबसे खतरनाक गलतियों पर चर्चा करेंगे, जो एप्लिकेशन को क्रैश कर सकती हैं, डेटा हानि का कारण बन सकती हैं और कोड को अपठनीय बना सकती हैं। आप न केवल यह जानेंगे कि गलत तरीका क्या है, बल्कि प्रत्येक मामले के लिए कार्यशील पैटर्न भी प्राप्त करेंगे। चलिए शुरू करते हैं!
\n\nगलती #1: ConcurrentModificationException — "चलते-चलते हटाना"
\n\nसमस्या क्या है?
\nसबसे क्लासिक गलती। आप HashMap पर इटरेट कर रहे हैं और map.remove(key) के माध्यम से एक एलिमेंट को हटाने का प्रयास कर रहे हैं। 90% मामलों में, इसके परिणामस्वरूप ConcurrentModificationException होता है।
// ❌ खतरनाक: यह अपवाद फेंकेगा!\nHashMap<String, Integer> scores = new HashMap<>();\nscores.put("Alice", 90);\nscores.put("Bob", 50);\nscores.put("Charlie", 70);\n\nfor (String name : scores.keySet()) {\n if (scores.get(name) < 60) {\n scores.remove(name); // ConcurrentModificationException!\n }\n}\n\n\nऐसा क्यों होता है?
\nHashMap का आंतरिक इटरेटर एक संशोधन काउंटर (modCount) रखता है। जब आप सीधे कलेक्शन के माध्यम से कोई एलिमेंट हटाते हैं, तो काउंटर बदल जाता है, लेकिन इटरेटर को इसके बारे में पता नहीं चलता। अगले इटरेशन पर, यह जाँच करता है: "अरे, काउंटर मेल नहीं खा रहा! अपवाद फेंको।"
इसे कैसे ठीक करें?
\nअपनी remove() विधि के साथ Iterator का उपयोग करें:
// ✅ सही: Iterator का उपयोग करें\nHashMap<String, Integer> scores = new HashMap<>();\nscores.put("Alice", 90);\nscores.put("Bob", 50);\nscores.put("Charlie", 70);\n\nIterator<Map.Entry<String, Integer>> iterator = scores.entrySet().iterator();\nwhile (iterator.hasNext()) {\n Map.Entry<String, Integer> entry = iterator.next();\n if (entry.getValue() < 60) {\n iterator.remove(); // सुरक्षित निष्कासन!\n }\n}\n\n\nJava 8 के साथ एक और भी सुंदर तरीका आया — removeIf():
// ✅ और भी बेहतर: removeIf (Java 8+)\nscores.entrySet().removeIf(entry -> entry.getValue() < 60);\n\n\nगलती #2: keySet() और get() के माध्यम से धीमा ट्रैवर्सल
\n\nसमस्या क्या है?
\nकई नौसिखिए ऐसा करते हैं:
\n\n// ❌ धीमा: keySet() + get() — दोहरी खोज\nfor (String key : map.keySet()) {\n System.out.println("Key: " + key + ", Value: " + map.get(key));\n}\n\n\nपहली नज़र में यह काम करता है। लेकिन समस्या यह है कि map.get(key) आंतरिक रूप से फिर से हैश खोजता है और बकेट को पार करता है। लूप के प्रत्येक चक्कर में, आप HashMap पर दो कॉल कर रहे हैं, एक के बजाय। छोटे मैप के लिए यह ध्यान देने योग्य नहीं है, लेकिन सैकड़ों हज़ारों एंट्रीज़ के लिए, यह 30-40% तक प्रदर्शन हानि है।
इसे कैसे ठीक करें?
\nहमेशा entrySet() का उपयोग करें — यह एक ही पास में "कुंजी-मान" जोड़े लौटाता है:
// ✅ तेज़: entrySet() — एक ही पास\nfor (Map.Entry<String, Integer> entry : map.entrySet()) {\n System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());\n}\n\n\nया Java 8 के साथ forEach का उपयोग करें:
// ✅ संक्षिप्त: forEach (Java 8+)\nmap.forEach((key, value) -> {\n System.out.println("Key: " + key + ", Value: " + value);\n});\n\n\nगलती #3: एलिमेंट के क्रम में भ्रम
\n\nसमस्या क्या है?
\nआपने कोड लिखा है जो HashMap को ट्रैवर्स करता है, और आप उम्मीद करते हैं कि एलिमेंट उसी क्रम में होंगे जिसमें वे डाले गए थे। लेकिन आपको पूरी तरह से अलग अनुक्रम मिलता है। इससे बग होते हैं, जो