[英]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.