并发修改例外:添加到ArrayList

Hou*_*man 45 java iteration android exception arraylist

问题发生在

Element element = it.next();
Run Code Online (Sandbox Code Playgroud)

并且包含该行的代码位于 OnTouchEvent

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;

        }           
    }
}
Run Code Online (Sandbox Code Playgroud)

所有这一切都在里面synchronized(mElements),这里mElements是一个ArrayList<Element>

当我触摸它时Element,它可能会激活cFlag,这将创建另一个Element具有不同属性的属性,这些属性将从屏幕上掉落并在不到一秒的时间内自行消失.这是我创建粒子效果的方式.我们可以称之为"粒子" crack,就像构造函数中的String参数一样.

这一切都正常,直到我添加另一个主要Element.现在我同时Elements在屏幕上有两个,如果我触摸最新的Element,它工作正常,并启动粒子.

但是,如果我触摸并激活cFlag旧版本Element,那么它会给我一个例外.

 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)
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

use*_*421 68

在遍历列表时修改列表(通过添加或删除元素)时发生ConcurrentModificationExceptionIterator.

尝试

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 );
Run Code Online (Sandbox Code Playgroud)

另外你应该考虑Jon建议的每个循环增强.

  • @AmitTumkur 尝试使用“iterator.remove();” 而不是“list.remove(object)”:) (2认同)

kon*_*mik 50

我通常使用这样的东西:

for (Element element : new ArrayList<Element>(mElements)) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

快速,干净,无错误

另一种选择是使用CopyOnWriteArrayList

  • 不,它不会指向同一个列表.列表被复制. (5认同)

Jon*_*eet 19

在迭代它时,不允许向集合添加条目.

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

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

for (Element element : mElements) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信增强的 for 循环在后台使用迭代器,因此使用它*应该*会导致同样的问题。 (3认同)
  • @OWiz:这两个表单将编译成基本相同的代码. (2认同)

Ixx*_*Ixx 12

索引for循环也应该有效.

for (int i = 0; i < collection.size(); i++)
Run Code Online (Sandbox Code Playgroud)

  • 你最终可能会陷入无限循环. (5认同)