[英]Concurrent Modification Exception : adding to an ArrayList
问题发生在
Element element = it.next();
包含该行的这段代码位于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;
}
}
}
所有这些都在synchronized(mElements)
,其中mElements
是一个ArrayList<Element>
当我触摸一个Element
,它可能会激活cFlag
,这将创建另一个具有不同属性的Element
,它会在不到一秒钟的时间内从屏幕上掉下来并自行销毁。 这是我创建粒子效果的方式。 我们可以称之为“粒子” crack
,就像构造函数中的String参数一样。
这一切正常,直到我添加另一个 main 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)
我怎样才能使这项工作?
ConcurrentModificationException当您在使用Iterator
遍历列表时修改列表(通过添加或删除元素)时发生。
尝试
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 的建议考虑对每个循环进行增强。
我通常使用这样的东西:
for (Element element : new ArrayList<Element>(mElements)) {
...
}
快速、干净且无错误
另一种选择是使用 CopyOnWriteArrayList
在迭代集合时,不允许将条目添加到集合中。
一种选择是在迭代mElements
时为新条目创建一个新List<Element>
,然后将所有新条目添加到mElement
( mElements.addAll(newElements)
)。 当然,这意味着您不会为那些新元素执行循环体——这是一个问题吗?
同时,我建议您更新代码以使用增强的 for 循环:
for (Element element : mElements) {
...
}
索引的 for 循环也应该工作。
for (int i = 0; i < collection.size(); i++)
使用迭代器还可以解决并发问题,如下所示:
Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
it.remove();
}
在这种情况下从列表中添加会导致 CME,任何synchronized
都不会让您避免这种情况。 相反,请考虑使用迭代器添加...
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 ...
好吧,在我在适配器中迭代列表的情况下,我已经尝试了所有方面,但是由于一次又一次的点击,我向我展示了抛出异常的消息。 我尝试将列表投射到
= (CopyOnWriteArraylist<MyClass>)mylist.value;
但它也给我抛出了一个 CannotCastException 的异常(我终于思考了一个事实,即为什么他们使用或为我们提供铸造的功能)。
我什至也使用了所谓的同步块,但即使它也不起作用,否则我可能会以错误的方式使用它。
因此,当我最终使用 #all of time# 处理 try catch 块中的异常的技术时,一切都结束了,并且它起作用了 所以把你的代码放在
try{
//block
}catch(ConcurrentModificationException){
//thus handling my code over here
}
我解决了创建锁(Kotlin)的问题:
import java.util.concurrent.locks.ReentrantLock
Class A {
private val listLock = ReentrantLock()
fun doSomething(newElement){
listLock.lock()
list.add(newElement)
listLock.unlock()
}
}
您可以使用自动递减for
循环,并在下次处理其他元素。
List additionalElements = new ArrayList();
for(int i = mElements.size() - 1; i > -1 ; i--){
//your business
additionalElements.add(newElement);
}
mElements.add(additionalElements);
公认的解决方案(创建集合的副本)通常效果很好。
但是,如果Element
包含另一个集合,则不会进行深复制!
例子:
class Element {
List<Kid> kids;
getKids() {
return kids;
}
}
现在,当您创建元素列表的副本时:
for (Element element : new ArrayList<Element>(elements)) { ... }
如果您迭代element.getKids()
并且相应地更改该元素的kids
,您仍然可以获得ConcurrentModificationException
。
回想起来很明显,但我最终进入了这个线程,所以也许这个提示对其他人也有帮助:
class Element {
List<Kid> kids;
getKids() {
// Return a copy of the child collection
return new ArrayList<Kid>(kids);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.