56 votes

Exception de modification simultanée : ajout à une ArrayList

Le problème se produit à

Element element = it.next();

Et ce code qui contient cette ligne, se trouve à l'intérieur d'un fichier 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;

        }           
    }
}

Tout ceci est à l'intérieur synchronized(mElements) , donde mElements est un ArrayList<Element>

Quand je touche un Element il peut activer cFlag ce qui créera une autre Element avec des propriétés différentes, qui tombera de l'écran et se détruira en moins d'une seconde. C'est ma façon de créer des effets de particules. Nous pouvons appeler cela "particule" crack comme le paramètre String dans le constructeur.

Tout cela fonctionne bien jusqu'à ce que j'ajoute un autre principal Element . Maintenant, j'ai deux Elements sur l'écran en même temps, et si je touche la plus récente Element cela fonctionne bien, et lance les particules.

Cependant, si je touche et active cFlag sur les anciens Element puis il me donne l'exception.

 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)

Comment puis-je faire en sorte que cela fonctionne ?

1voto

David Bemerguy Points 851

L'utilisation d'itérateurs permet également de résoudre les problèmes de concurrence, comme ceci :

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

1voto

J'ai essayé tous les aspects dans mon cas où j'ai itéré dans un adaptateur une liste mais en raison de frapper encore et encore je me suis montré le message de l'exception étant jeté. J'ai essayé de convertir la liste en

 = (CopyOnWriteArraylist<MyClass>)mylist.value;

mais il m'a aussi envoyé une exception de CouldNotCastException, (et j'ai finalement réfléchi au fait que pourquoi ils utilisent ou nous fournissent une fonctionnalité de casting).

J'ai même utilisé le bloc synchronisé, mais cela n'a pas fonctionné, sinon j'aurais pu l'utiliser de la mauvaise façon.

Ainsi, c'est quand j'ai finalement utilisé la technique #toujours# de gérer l'exception dans le bloc try catch, et ça a marché Donc mettez votre code dans le bloc

try{
//block

}catch(ConcurrentModificationException){
//thus handling my code over here
}

1voto

Saren Points 21

Vous pourriez utiliser une auto-décroissance for boucle, et traiter les éléments supplémentaires la prochaine fois.

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

1voto

hb0 Points 426

La solution acceptée (créer une copie de la collection) est généralement la suivante fonctionne bien .

Toutefois, si le Element contient une autre collection cela ne fait pas un copie profonde ¡!

Exemple :

class Element {
   List<Kid> kids;

   getKids() {
      return kids;
   }
}

Maintenant, lorsque vous créez une copie de la liste des éléments :

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

Vous pouvez toujours obtenir un ConcurrentModificationException si vous itérez sur element.getKids() et, parallèlement, modifier le kids de cet élément.

Avec le recul, c'est évident, mais je me suis retrouvé dans ce fil de discussion, alors peut-être que cette astuce aidera d'autres personnes :

class Element {
   List<Kid> kids;

   getKids() {
      // Return a copy of the child collection
      return new ArrayList<Kid>(kids);
   }
}

0voto

Marco Points 98

J'ai résolu la création d'un verrou (Kotlin) :

import java.util.concurrent.locks.ReentrantLock

Class A {
    private val listLock = ReentrantLock()
    fun doSomething(newElement){
        listLock.lock()
        list.add(newElement)
        listLock.unlock()
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X