जावा में संग्रह से तत्वों को हटाना: ConcurrentModificationException से कैसे बचें

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

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

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

"मैं लूप में सूची से एक तत्व हटाना चाहता हूँ — और मुझे ConcurrentModificationException मिलता है"। परिचित लगता है?

\n

यदि आपने कभी Java में कोड लिखा है और ArrayList या HashMap से इटरेशन के दौरान (उदाहरण के लिए, for-each लूप में) एक तत्व हटाने का प्रयास किया है, तो आपने निश्चित रूप से यह भयानक लाल अपवाद देखा होगा। यह सबसे अनुपयुक्त क्षण में प्रकट होता है, मानो Java आपसे कह रहा हो: "अरे, तुम क्या कर रहे हो? जब मैं उस पर चल रहा हूँ तो संग्रह को मत छुओ!"।

\n\n

Stack Overflow पर इस प्रश्न को हज़ारों बार देखा गया है। और यह कोई संयोग नहीं है: समस्या सरल लगती है, लेकिन इसके पीछे ऑप्टिमाइज़ेशन और मल्टीथ्रेडिंग की बारीकियों की एक पूरी परत छिपी है। इस लेख में हम विस्तार से जानेंगे कि Java में लूप में संग्रह से ऑब्जेक्ट को सही तरीके से कैसे हटाएं, बिना ConcurrentModificationException उत्पन्न किए, और प्रदर्शन को खोए बिना।

\n\n

चलिए शुरू करते हैं!

\n\n

ConcurrentModificationException क्यों होता है?

\n

संक्षिप्त उत्तर: fail-fast इटरेटर. Java Collections Framework (ArrayList, HashSet, HashMap, आदि) के अधिकांश संग्रह परिवर्तनों की आंतरिक गणना (modCount) का उपयोग करते हैं। जब आप एक इटरेटर बनाते हैं, तो यह modCount के वर्तमान मान को याद रखता है। प्रत्येक next() कॉल पर, इटरेटर जाँचता है कि क्या संग्रह बाहर से बदल गया है (संग्रह के add() या remove() विधियों के माध्यम से)। यदि गणना मेल नहीं खाती है — तो अपवाद फेंक दिया जाता है।

\n\n

कोड का उदाहरण जो निश्चित रूप से विफल होगा:

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nfor (String item : list) {\n    if ("B".equals(item)) {\n        list.remove(item); // ConcurrentModificationException!\n    }\n}\n
\n

क्यों? क्योंकि for-each अंतर्निहित रूप से एक इटरेटर का उपयोग करता है, और list.remove() सीधे modCount को बदलता है, इटरेटर को दरकिनार करते हुए। इटरेटर इसे माफ नहीं करता।

\n\n

विधि #1: Iterator.remove() का उपयोग करें — क्लासिक

\n

सबसे सरल और सही तरीका है कि इटरेटर को स्पष्ट रूप से लें और इसकी remove() विधि को कॉल करें। यह विधि इटरेटर के आंतरिक काउंटर को संग्रह के साथ सिंक्रोनाइज़ करती है।

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nIterator<String> iterator = list.iterator();\nwhile (iterator.hasNext()) {\n    String item = iterator.next();\n    if ("B".equals(item)) {\n        iterator.remove(); // सुरक्षित!\n    }\n}\nSystem.out.println(list); // [A, C, D]\n
\n

लाभ: सुरक्षित, सभी संग्रहों के लिए काम करता है जो इटरेटर का समर्थन करते हैं। नुकसान: थोड़ा लंबा है। सरल फ़िल्टरिंग के लिए, अधिक आधुनिक दृष्टिकोणों का उपयोग किया जा सकता है।

\n\n

विधि #2: Collection.removeIf() — आधुनिक और सुरुचिपूर्ण

\n

Java 8 से शुरू करके, अधिकांश संग्रहों में removeIf(Predicate filter) विधि है। यह शर्त के अनुसार तत्वों को हटाने का सबसे संक्षिप्त तरीका है।

\n
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));\nlist.removeIf(item -> "B".equals(item));\nSystem.out.println(list); // [A, C, D]\n
\n

आंतरिक रूप से, removeIf एक इटरेटर (या इंडेक्स को संरक्षित करते हुए सीधा पास) का उपयोग करता है, इसलिए कोई ConcurrentModificationException नहीं होगा। यह सरल मामलों के लिए अनुशंसित तरीका है।

\n

प्रदर्शन: ArrayList के लिए, removeIf विधि O(n) में काम करती है, लेकिन इसे कुशलता से करती है — यह एक पास में तत्वों को स्थानांतरित करती है, न कि एक-एक करके हटाती है (जो O(n²) होता)।

\n\n

विधि #3: इंडेक्स द्वारा रिवर्स पास (List के लिए)

\n

यदि किसी कारण से आप इटरेटर का उपयोग नहीं कर सकते (उदाहरण के लिए, आपको एक साथ कई तत्वों को बदलने की आवश्यकता है या आप पुराने कोड के साथ काम कर रहे हैं), तो आप सामान्य for लूप का उपयोग करके सूची को उल्टे क्रम में पार कर सकते हैं।

List<String> list = new A                

ब्लॉग

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