मुझे इस उदाहरण में java.util.ConcurrentModificationException क्यों नहीं मिल रही है?


176

नोट: मुझे Iterator#remove()विधि की जानकारी है ।

निम्नलिखित कोड नमूने में, मुझे समझ नहीं आया कि पद्धति List.removeमें क्यों mainफेंकता है ConcurrentModificationException, लेकिन विधि में नहींremove

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}

3
किसी सूची से किसी तत्व को हटाने का एकमात्र सुरक्षित तरीका उस सूची पर पुनरावृति करना है Iterator#remove()। आप इसे इस तरह क्यों कर रहे हैं?
मैट बॉल

@MattBall: मैं बस यह देखने की कोशिश कर रहा था कि यहाँ क्या कारण हो सकता है। क्योंकि, यह दोनों विधियों में समान "लूप के लिए बढ़ाया गया" है, लेकिन एक फेंकता है ConcurrentModificationExceptionऔर दूसरा नहीं।
भेश गुरुंग

आपके द्वारा हटाए जाने वाले तत्व में अंतर होता है। विधि में आपका 'मध्य तत्व' हटा दिया जाता है। मुख्य में आप अंतिम निकालते हैं। यदि आप संख्या को स्वैप करते हैं तो आपको अपनी विधि में अपवाद मिलता है। अभी भी यकीन नहीं है कि हालांकि क्यों है।
बेन वैन गोम्पेल

मुझे इसी तरह की समस्या थी, जब मेरे लूप ने भी एक स्थिति खोली जो लूप में एक आइटम को हटाने के बाद मौजूद नहीं थी। मैंने बस return;इसे लूप में जोड़कर तय किया ।
22

java8 एंड्रॉइड पर, पिछले एक के अलावा अन्य एलिमेंट को हटाकर कॉनकॉन्ट्रोमोडिफिकेशन अपवाद को लागू करेगा। तो आपके मामले के लिए, हटाए जाने वाले फ़ंक्शन को एक अपवाद मिलेगा जो कि आपके पहले देखे गए विपरीत है।
गोंगलोंग

जवाबों:


262

यहाँ क्यों है: जैसा कि जवादोक में कहा गया है:

इस वर्ग के पुनरावृत्त और सूचीकर्ता विधियों द्वारा दिए गए पुनरावृत्तियाँ विफल-तेज़ हैं: यदि पुनरावृत्त बनाने के बाद सूची को किसी भी समय संरचनात्मक रूप से संशोधित किया जाता है, तो किसी भी तरह से पुनरावृत्तिकर्ता स्वयं को हटाने या जोड़ने के तरीकों के अलावा, यह शोधकर्ता एक समवर्तीमॉडिफिकेशन अपवाद को फेंक देगा।

यह चेक next()इट्रेटर की विधि में किया जाता है (जैसा कि आप स्टैकट्रेस द्वारा देख सकते हैं)। लेकिन हम next()केवल विधि तक पहुंचेंगे यदि hasNext()सच दिया गया है, जो कि सीमा को पूरा करने के लिए प्रत्येक को जांचने के लिए कहा जाता है। आपकी निष्कासन विधि में, जब hasNext()यह जांचता है कि क्या उसे किसी अन्य तत्व को वापस करने की आवश्यकता है, तो यह देखेगा कि उसने दो तत्व वापस किए हैं, और अब एक तत्व को हटाए जाने के बाद सूची में केवल दो तत्व शामिल हैं। तो सब आड़ू है और हम पुनरावृति के साथ किया जाता है। समवर्ती संशोधनों के लिए जांच नहीं होती है, क्योंकि यह उस next()विधि में किया जाता है जिसे कभी नहीं कहा जाता है।

इसके बाद हम दूसरे लूप में आते हैं। दूसरे नंबर को हटाने के बाद hasNext विधि फिर से जाँच करेगी कि क्या अधिक मान वापस आ सकते हैं। इसने पहले ही दो मान लौटा दिए हैं, लेकिन सूची में अब केवल एक ही है। लेकिन यहाँ कोड है:

public boolean hasNext() {
        return cursor != size();
}

1! = 2, इसलिए हम इस next()पद्धति को जारी रखते हैं , जो अब महसूस करता है कि कोई व्यक्ति सूची में गड़बड़ी कर रहा है और अपवाद को निकालता है।

आशा है कि आपके सवाल को साफ करता है।

सारांश

List.remove()ConcurrentModificationExceptionसूची से दूसरा अंतिम तत्व निकालते समय इसे फेंका नहीं जाएगा ।


5
@pushy: केवल वही उत्तर दें जो उत्तर देने के लिए लगता है कि प्रश्न वास्तव में क्या पूछ रहा है, और स्पष्टीकरण अच्छा है। मैं इस जवाब को स्वीकार कर रहा हूं और +1 भी। धन्यवाद।
भेश गुरुंग

42

यदि इसे लागू करने की एक प्रति Collection(संग्रह खुद नहीं) से कुछ निकालने के लिए इसे संभालने का एक तरीका है । Cloneएक के माध्यम से एक प्रतिलिपि बनाने के लिए मूल संग्रह Constructor

इस अपवाद को उन विधियों द्वारा फेंका जा सकता है जिन्होंने किसी वस्तु के समवर्ती संशोधन का पता लगाया है जब ऐसा संशोधन अनुमेय नहीं है।

आपके विशिष्ट मामले के लिए, सबसे पहले, मुझे नहीं लगता finalहै कि इस पर विचार करने का एक तरीका है कि आप सूची में पिछले घोषणा को संशोधित करने का इरादा रखते हैं

private static final List<Integer> integerList;

मूल सूची के बजाय एक प्रति संशोधित करने पर भी विचार करें।

List<Integer> copy = new ArrayList<Integer>(integerList);

for(Integer integer : integerList) {
    if(integer.equals(remove)) {                
        copy.remove(integer);
    }
}

14

आइटम निकालते समय फ़ॉर्वर्ड / इट्रेटर विधि काम नहीं करती है। आप त्रुटि के बिना तत्व को हटा सकते हैं, लेकिन जब आप हटाए गए आइटम तक पहुंचने का प्रयास करेंगे तो आपको एक रनटाइम त्रुटि मिलेगी। आप पुनरावृत्ति का उपयोग नहीं कर सकते क्योंकि पुशी के रूप में यह एक समवर्ती पहचान का कारण बनेगा, इसलिए इसके बजाय लूप के लिए एक नियमित उपयोग करें, लेकिन इसके माध्यम से पीछे की ओर कदम बढ़ाएं।

List<Integer> integerList;
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

int size= integerList.size();

//Item to remove
Integer remove = Integer.valueOf(3);

एक तरकीब:

यदि आप किसी सूची तत्व को निकालने जा रहे हैं, तो सरणी को उल्टे क्रम में पीछे खींचें। बस सूची के माध्यम से पीछे की ओर जाने से आप हटाए गए आइटम पर जाने से बचते हैं, जो अपवाद को हटा देता है।

//To remove items from the list, start from the end and go backwards through the arrayList
//This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
//for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
for (int i=size-1; i> -1; i--) {
    if (integerList.get(i).equals(remove) ) {
        integerList.remove(i);
    }
}

कमाल का विचार !
dobrivoje

7

यह स्निपेट हमेशा एक समवर्ती मौद्रिकरण अपवाद को फेंक देगा ।

नियम यह है कि आप एक Iterator (जो तब होता है जब आप प्रत्येक के लिए एक लूप का उपयोग करते हैं) का उपयोग करते हुए उस पर पुनरावृत्ति करते हुए (सूची से तत्वों को जोड़ने या हटाने) को संशोधित नहीं कर सकते हैं।

javadocs:

इस वर्ग के पुनरावृत्त और सूचीकर्ता विधियों द्वारा दिए गए पुनरावृत्तियाँ विफल-तेज़ हैं: यदि पुनरावृत्त बनाने के बाद सूची को किसी भी समय संरचनात्मक रूप से संशोधित किया जाता है, तो किसी भी तरह से पुनरावृत्तिकर्ता के स्वयं के निष्कासन या जोड़ने के तरीकों के अलावा, यह शोधकर्ता एक समवर्तीमॉडिफिकेशन अपवाद को फेंक देगा।

इसलिए यदि आप सूची (या सामान्य रूप से किसी भी संग्रह) को संशोधित करना चाहते हैं, तो पुनरावृत्ति का उपयोग करें, क्योंकि तब यह संशोधनों के बारे में पता है और इसलिए उन्हें ठीक से संभाला जाएगा।

उम्मीद है की यह मदद करेगा।


3
ओपी स्पष्ट रूप से बताता है कि लूप्स में से एक अपवाद नहीं फेंकता है और अभिमानी ऐसा क्यों हुआ था।
madth3

do इंटररस्टिंग ’से आपका क्या मतलब है?
भूषण

4

मेरे पास भी यही समस्या थी लेकिन मामले में मैं इसे पुनरावृत्त सूची में शामिल कर रहा था। मैंने इसे इस तरह से बनाया है

public static void remove(Integer remove) {
    for(int i=0; i<integerList.size(); i++) {
        //here is maybe fine to deal with integerList.get(i)==null
        if(integerList.get(i).equals(remove)) {                
            integerList.remove(i);
        }
    }
}

अब सब कुछ ठीक हो जाता है क्योंकि आप अपनी सूची में कोई भी इटेरेटर नहीं बनाते हैं, आप इसे "मैन्युअल रूप से" टाइप करते हैं। और शर्त i < integerList.size()कभी भी आपको मूर्ख नहीं बनाएगी क्योंकि जब आप सूची में कमी / वृद्धि के सूची आकार में कुछ हटाते / जोड़ते हैं ..

आशा है कि यह मदद करता है, मेरे लिए कि समाधान था।


यह सच नहीं है ! साक्ष्य: परिणाम देखने के लिए इस स्निपेट को चलाएं: सार्वजनिक स्थैतिक शून्य मुख्य (स्ट्रिंग ... आर्ग्स) {सूची <स्ट्रिंग> सूचीऑफबुक = नया एरेलेस्ट <> (); listOfBooks.add ("कोड पूरा"); listOfBooks.add ("कोड 22"); listOfBooks.add ("22 प्रभावी"); listOfBooks.add ("नेटबीन्स 33"); System.err.println ("हटाने से पहले:" + listOfBooks); for (int index = 0; index; list <fBooks.size (); index ++) {if (listOfBooks.get (index)) .contains ("22")) {listOfBooks.remove (index); }} System.err.println ("हटाने के बाद:" + listOfBooks); }
dobrivoje

1

यदि आप कॉपी-ऑन-राइट संग्रह का उपयोग करते हैं तो यह काम करेगा; हालाँकि जब आप list.iterator () का उपयोग करते हैं, तो लौटा हुआ Iterator हमेशा तत्वों के संग्रह का संदर्भ देगा क्योंकि यह तब था जब (नीचे) list.iterator () कहा जाता था, भले ही कोई अन्य संग्रह संग्रह को संशोधित करता हो। कॉपी-ऑन-राइट-आधारित Iterator या ListIterator (जैसे ऐड, सेट, या निकालें) पर कॉल किए गए किसी भी उत्परिवर्तन विधियाँ एक UnsupportedOperationException को फेंक देगी।

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new CopyOnWriteArrayList<>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

0

यह जावा 1.6 पर ठीक चलता है

~% javac RemoveListElementDemo.java
~% java RemoveListElementDemo
~% cat RemoveListElementDemo.java

import java.util.*;
public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

~%


टाइपो के बारे में क्षमा करें यह जावा 1.6 पर 'रन' ठीक है
बटोसाई

हम्म ... हो सकता है कि आपका एक अलग कार्यान्वयन हो। लेकिन विनिर्देश के अनुसार ऐसा करना चाहिए, IMO। @ पुष्य का जवाब देखिए।
भेश गुरुंग

दुर्भाग्य से, आईडी जावा 1.8 पर नहीं है
dobrivoje

0

मेरे मामले में मैंने इसे इस तरह किया:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());

0

इटरेटर बदलें for eachमें for loopहल करने के लिए।

और कारण है:

इस वर्ग के पुनरावृत्त और सूचीकर्ता विधियों द्वारा दिए गए पुनरावृत्तियाँ विफल-तेज़ हैं: यदि पुनरावृत्त बनाने के बाद सूची को किसी भी समय संरचनात्मक रूप से संशोधित किया जाता है, तो किसी भी तरह से पुनरावृत्तिकर्ता स्वयं को हटाने या जोड़ने के तरीकों के अलावा, यह शोधकर्ता एक समवर्तीमॉडिफिकेशन अपवाद को फेंक देगा।

- पसंदीदा जावा डॉक्स।


-1

अपना कोड मैन चेक करो…।

मुख्य विधि में आप 4 तत्व को हटाने की कोशिश कर रहे हैं जो कि नहीं है और इसलिए त्रुटि है। हटाने () विधि में आप 3 तत्व को हटाने की कोशिश कर रहे हैं जो वहां है और इसलिए कोई त्रुटि नहीं है।


आप गलत हैं: संख्या 2और 3सूची के लिए सूचकांक नहीं हैं, लेकिन तत्व हैं। दोनों हटाने equalsसूची तत्वों के खिलाफ तर्क की जाँच करता है , तत्वों का सूचकांक नहीं। इसके अलावा, अगर यह सूचकांक संबंधित था, तो यह होगा IndexOutOfBoundsException, नहीं ConcurrentModificationException
माल्टे हार्टविग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.