java-并发修改异常:添加到ArrayList

问题发生在

Element element = it.next();

包含该行的代码在Element的内部

for (Iterator<Element> it = mElements.iterator(); it.hasNext();){
    Element element = it.next();

    if(touchX > element.mX  && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY   
            && touchY < element.mY + element.mBitmap.getHeight()) {  

        //irrelevant stuff..

        if(element.cFlag){
            mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
            element.cFlag = false;

        }           
    }
}

所有这些都在Element内部,其中ElementsElement

当我触摸Element时,它可能会激活Elements,这将创建另一个具有不同属性的Element,它们会从屏幕上掉落并在不到一秒钟的时间内毁坏自己。 这是我创建粒子效果的方式。 我们可以将其称为“粒子” crack,就像构造函数中的String参数一样。

一切正常,直到我添加另一个主Element。现在我同时在屏幕上有两个Elements,如果我触摸最新的Element,它会正常工作并发射粒子。

但是,如果我触摸并激活了旧版Element上的cFlag,则它给了我例外。

 07-28 15:36:59.815: ERROR/AndroidRuntime(4026): FATAL EXCEPTION: main
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): java.util.ConcurrentModificationException
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.Juggle2.Panel.onTouchEvent(Panel.java:823)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.view.View.dispatchTouchEvent(View.java:3766)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1767)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1119)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1751)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1785)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.os.Looper.loop(Looper.java:123)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at android.app.ActivityThread.main(ActivityThread.java:4627)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at java.lang.reflect.Method.invokeNative(Native Method)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at java.lang.reflect.Method.invoke(Method.java:521)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:651)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026):     at dalvik.system.NativeStart.main(Native Method)

我该如何工作?

Houseman asked 2020-07-10T16:43:23Z
9个解决方案
66 votes

当您使用Iterator遍历列表时(通过添加或删除元素)修改列表时,会发生ConcurrentModificationException。

尝试

List<Element> thingsToBeAdd = new ArrayList<Element>();
for(Iterator<Element> it = mElements.iterator(); it.hasNext();) {
    Element element = it.next();
    if(...) {  
        //irrelevant stuff..
        if(element.cFlag){
            // mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
            thingsToBeAdd.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
            element.cFlag = false;
        }           
    }
}
mElements.addAll(thingsToBeAdd );

同样,您应该按照Jon的建议考虑对每个循环进行增强。

user802421 answered 2020-07-10T16:43:46Z
46 votes

我通常使用这样的东西:

for (Element element : new ArrayList<Element>(mElements)) {
    ...
}

快速,干净且无错误

另一个选择是使用CopyOnWriteArrayList

konmik answered 2020-07-10T16:44:14Z
19 votes

在迭代集合时,不允许将其添加到集合中。

一种选择是在迭代mElements时为新条目创建新的List<Element>,然后将所有新条目添加到mElementmElements.addAll(newElements))。 当然,这意味着您不会为这些新元素执行循环主体-这是问题吗?

同时,我建议您更新代码以使用增强的for循环:

for (Element element : mElements) {
    ...
}
Jon Skeet answered 2020-07-10T16:44:44Z
12 votes

索引的for循环也应该起作用。

for (int i = 0; i < collection.size(); i++)
Ixx answered 2020-07-10T16:45:05Z
2 votes

在这种情况下,从列表中添加会导致CME,那么没有Element element = it.next();可以让您避免这种情况。 相反,请考虑使用迭代器进行添加...

        for(ListIterator<Element> it = mElements.listIterator(); it.hasNext();){
            Element element = it.next();

            if(touchX > element.mX  && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY   
                    && touchY < element.mY + element.mBitmap.getHeight()) {  

                //irrelevant stuff..

                if(element.cFlag){
                    // mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
                    it.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
                    element.cFlag = false;

                }           
            }
        }

我也认为这样说有点滑

...问题发生在Element element = it.next();

为了精确起见,请注意上述内容并不能保证。

API文档指出,无法保证这种...行为,因为通常来说,在存在不同步的并发修改的情况下,不可能做出任何严格的保证。 快速执行操作会尽最大努力抛出ConcurrentModificationException ...

gnat answered 2020-07-10T16:45:43Z
1 votes

使用迭代器还可以解决并发问题,如下所示:

Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
    it.remove();
}
David Bemerguy answered 2020-07-10T16:46:03Z
1 votes

好吧,我尝试了在适配器中循环访问列表的所有方面,但是由于一次又一次的点击,我向我展示了引发异常的消息。我尝试将列表投射到

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

但这也使我抛出了CouldNotCastException的异常(并且我终于思考了一个事实,为什么它们使用或为我们提供了铸造的便利)。

我什至也使用了所谓的同步块,但是即使它没有用,或者我可能会以错误的方式使用它。

因此,这就是我终于使用“所有时间”处理异常的全部技巧在try catch块中,它起作用了因此,将您的代码放入

try{
//block

}catch(ConcurrentModificationException){
//thus handling my code over here
}
Siddharth Choudhary answered 2020-07-10T16:46:37Z
1 votes

您可以使用自动递减for循环,并在下次处理其他元素。

List additionalElements = new ArrayList();
for(int i = mElements.size() - 1; i > -1 ; i--){
    //your business
    additionalElements.add(newElement);
}
mElements.add(additionalElements);
Saren answered 2020-07-10T16:46:57Z
0 votes

我解决了创建锁(Kotlin)的问题:

import java.util.concurrent.locks.ReentrantLock

Class A {
    private val listLock = ReentrantLock()
    fun doSomething(newElement){
        listLock.lock()
        list.add(newElement)
        listLock.unlock()
    }
}
Marco answered 2020-07-10T16:47:17Z
translate from https://stackoverflow.com:/questions/6866238/concurrent-modification-exception-adding-to-an-arraylist