web-dev-qa-db-ja.com

同時変更例外: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が作成され、画面から落ちて1秒以内に破壊されます。パーティクルエフェクトを作成する私の方法です。コンストラクターのStringパラメーターのように、この「パーティクル」crackを呼び出すことができます。

別のメインElementを追加するまで、これはすべて正常に機能します。これで、画面に2つのElementsが同時に表示されます。最新のElementをタッチすると、正常に動作し、パーティクルが起動します。

ただし、古いcFlagElementをタッチしてアクティブにすると、例外が発生します。

_ 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)
_

どうすればこの作品を作ることができますか?

43
Houseman

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が提案したように、各ループの拡張を検討する必要があります。

64
user802421

私は通常このようなものを使用します:

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

迅速、クリーン、バグフリー

別のオプションは、CopyOnWriteArrayListを使用することです

45
konmik

コレクションを反復している間、コレクションにエントリを追加することはできません。

1つのオプションは、mElementsを繰り返し処理している間に新しいエントリ用に新しい_List<Element>_を作成し、その後ですべての新しいものをmElementに追加します(mElements.addAll(newElements))。もちろん、それはそれらの新しい要素のループ本体を実行しなかったことを意味します-それは問題ですか?

同時に、 enhanced for loop を使用するようにコードを更新することをお勧めします。

_for (Element element : mElements) {
    ...
}
_
19
Jon Skeet

インデックス付きforループも機能するはずです。

for (int i = 0; i < collection.size(); i++)
12
Ixx

この場合、リストから追加すると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;

                }           
            }
        }
_

また、私はそれがややslipperyのように言うと思う...

...問題はElement element = it.next();で発生します

正確さのために、上記は保証されないことに注意してください。

APIドキュメンテーション は、この...動作は、一般的に言えば、非同期の同時変更が存在する場合にハードな保証を行うことは不可能なので、保証できないことを指摘しています。フェイルファースト操作は、ConcurrentModificationExceptionをベストエフォートベースでスローします...

2
gnat

自動デクリメントforループを使用して、次回追加要素を処理できます。

List additionalElements = new ArrayList();
for(int i = mElements.size() - 1; i > -1 ; i--){
    //your business
    additionalElements.add(newElement);
}
mElements.add(additionalElements);
1
Saren

イテレータを使用すると、次のように同時実行の問題も修正されます。

Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
    it.remove();
}
1
David Bemerguy

さて、アダプタでリストを繰り返していたケースのすべての側面を試しましたが、何度もヒットするため、例外がスローされているというメッセージが表示されました。リストをキャストしてみました

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

しかし、CouldNotCastExceptionの例外もスローされました(そして、私は最終的に、なぜキャストの機能を使用または提供するのかを熟考しました)。

いわゆる同期ブロックも使用しましたが、それでも機能しなかったし、間違った方法で使用していたかもしれません。

したがって、try catchブロックで例外を処理する#all of time#テクニックを最後に使用したのはすべてであり、それが機能したので、コードを

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()
    }
}
0
Marco