जावा में Map को ट्रैवर्स करना: बिना दर्द और गति खोए कैसे इटरेट करें

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

आपका कोड बिना किसी कारण के धीमा क्यों हो सकता है?

\n

कल्पना करें: आपने एक आदर्श एल्गोरिदम लिखा, सभी unit-टेस्ट पास कर लिए, लेकिन प्रोडक्शन में एप्लिकेशन अचानक "धीमा" होने लगता है। अक्सर इसका कारण एल्गोरिदम नहीं, बल्कि आपके द्वारा कलेक्शन को ट्रैवर्स करने का तरीका होता है। यह विशेष रूप से Java Map पर लागू होता है — एक डेटा संरचना जो 10 में से 9 कॉर्पोरेट प्रोजेक्ट्स में पाई जाती है।

\n

Stack Overflow पर सवाल "How do I efficiently iterate over each entry in a Java Map?" ने हजारों व्यूज़ एकत्र किए हैं, यह कोई संयोग नहीं है। कई डेवलपर्स, यहां तक कि 2-3 साल के अनुभव वाले भी, गैर-इष्टतम तरीकों का उपयोग करते हैं। आइए समझते हैं कि Map को सही तरीके से कैसे ट्रैवर्स करें, और क्यों एक तरीका दूसरे से 10 गुना तेज़ हो सकता है।

\n\n

Java Map की संरचना: इसके अंदर क्या है?

\n

इटरेशन के बारे में बात करने से पहले, इसकी संरचना को याद कर लेते हैं। HashMap — बकेट वाली हैश टेबल है। TreeMap — रेड-ब्लैक ट्री है। LinkedHashMap — डबली लिंक्ड लिस्ट वाला हाइब्रिड है। ये प्रत्येक संरचना key-value जोड़े संग्रहीत करती है, लेकिन इन्हें ट्रैवर्स करने के तरीके गति और मेमोरी खपत में भिन्न होते हैं।

\n

Map द्वारा प्रदान किए जाने वाले मुख्य व्यू कलेक्शन (view collections):

\n
    \n
  • keySet() — कुंजियों का सेट
  • \n
  • values() — मानों का कलेक्शन
  • \n
  • entrySet() — जोड़ियों (कुंजी-मान) का सेट
  • \n
\n

इन्हीं तीन "खिड़कियों" के माध्यम से हम डेटा को देखेंगे।

\n\n

तरीका #1: for-each के माध्यम से क्लासिक entrySet (सबसे लोकप्रिय)

\n

यह एक बुनियादी लेकिन बहुत प्रभावी पैटर्न है। आपको एक ही इटरेशन में कुंजी और मान दोनों तक पहुंच मिलती है।

\n
Map<String, Integer> map = new HashMap<>();\n// ... map भरना\nfor (Map.Entry<String, Integer> entry : map.entrySet()) {\n    String key = entry.getKey();\n    Integer value = entry.getValue();\n    // आपका लॉजिक\n}
\n

यह कुशल क्यों है? क्योंकि entrySet() डेटा की कॉपी नहीं, बल्कि एक view (दृश्य) लौटाता है। इटरेटर HashMap के आंतरिक नोड्स (Node) से गुज़रता है, और प्रत्येक next() कॉल तुरंत कुंजी और मान दोनों देता है। हैश द्वारा कोई अतिरिक्त खोज नहीं होती।

\n

कब उपयोग करें: जब भी जोड़ी के दोनों घटकों की आवश्यकता हो। Java समुदाय में यह डी-फैक्टो मानक है।

\n\n

तरीका #2: keySet + get (एंटीपैटर्न जिससे बचना चाहिए)

\n

कई शुरुआती इस तरह लिखते हैं:

\n
for (String key : map.keySet()) {\n    Integer value = map.get(key);\n    // लॉजिक\n}
\n

पहली नज़र में, कोड पढ़ने योग्य है। लेकिन अंदर से HashMap.get() हर बार कुंजी के हैश की पुनर्गणना करता है और कोलिज़न चेन से गुज़रता है। 100 तत्वों के लिए यह ध्यान देने योग्य नहीं है, लेकिन 100,000 के लिए यह गंभीर गिरावट है। सबसे खराब स्थिति में (कई कोलिज़न) जटिलता O(n) से O(n²) हो जाती है।

\n

निष्कर्ष: यदि आपको कुंजी और मान दोनों चाहिए — entrySet() का उपयोग करें। यदि केवल कुंजियाँ चाहिए — keySet() आदर्श है।

\n\n

तरीका #3: स्पष्ट निष्कासन के साथ इटरेटर (ट्रैवर्सल के दौरान म्यूटेशन के लिए)

\n

कभी-कभी इटरेशन के दौरान Map से तत्वों को हटाने की आवश्यकता होती है। for-each लूप ConcurrentModificationException फेंकेगा। यहाँ इटरेटर मदद करता है:

\n
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();\nwhile (iterator.hasNext()) {\n    Map.Entry<String, Integer> entry = iterator.next();\n    if (entry.getValue() < 0) {\n        iterator.remove(); // सुरक्षित निष्कासन\n    }\n}
\n

यह विधि विशेष रूप से तब उपयोगी होती है जब आप "चलते-फिरते" डेटा को फ़िल्टर कर रहे हों। इटरेटर HashMap में प्रत्येक निष्कासन पर O(1) में काम करता है, क्योंकि यह वर्तमान नोड को जानता है।

\n\n

तरीका #4: लैम्ब्डा के साथ forEach (Java 8+)

\n

Java 8 से शुरू करके, Map का अपना forEach मेथड है:

\n
map.forEach((key, value) -> {\n    System.out.println(key + " -> " + value);\n});
\n

हुड के नीचे

ब्लॉग

पुस्तक अनुशंसाएँ