Excepción de modificación concurrente: agregar a una ArrayList

El problema ocurre en

Element element = it.next(); 

Y este código que contiene esa línea, está dentro de un OnTouchEvent

 for (Iterator it = mElements.iterator(); it.hasNext();){ Element element = it.next(); if(touchX > element.mX && touchX  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; } } } 

Todo esto está dentro synchronized(mElements) , donde mElements es un ArrayList

Cuando toco un Element , puede activar cFlag , que creará otro Element con diferentes propiedades, que se caerá de la pantalla y se destruirá a sí mismo en menos de un segundo. Es mi forma de crear efectos de partículas. Podemos llamar a esta crack “partículas”, como el parámetro String en el constructor.

Todo esto funciona bien hasta que agregue otro Element principal. Ahora tengo dos Elements en la pantalla al mismo tiempo, y si toco el Element más nuevo, funciona bien y lanza las partículas.

Sin embargo, si toco y activo cFlag en el Element anterior, entonces me da la excepción.

  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) 

¿Cómo puedo hacer que esto funcione?

ConcurrentModificationException se produce cuando modifica la lista (agregando o eliminando elementos) al atravesar una lista con Iterator .

Tratar

 List thingsToBeAdd = new ArrayList(); for(Iterator 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 ); 

También debería considerar mejorar para cada ciclo como Jon sugirió.

Normalmente uso algo como esto:

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

rápido, limpio y libre de errores

otra opción es usar CopyOnWriteArrayList

No puede agregar una entrada a una colección mientras la itera.

Una opción es crear un nuevo List para nuevas entradas mientras itera sobre mElements , y luego agrega todas las nuevas a mElement después ( mElements.addAll(newElements) ). Por supuesto, eso significa que no habrá ejecutado el cuerpo del bucle para esos nuevos elementos, ¿es eso un problema?

Al mismo tiempo, recomiendo que actualice su código para usar el bucle for mejorado :

 for (Element element : mElements) { ... } 

Un bucle indexado también debería funcionar.

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

agregar de la lista en este caso conduce a CME, ninguna cantidad de synchronized le permitirá evitar eso. En su lugar, considere agregar usando el iterador …

  for(ListIterator 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; } } } 

También creo que es algo resbaladizo decir como ...

... El problema ocurre en Element element = it.next();

por el bien de la precisión, tenga en cuenta que lo anterior no está garantizado.

La documentación de API señala que este ... comportamiento no se puede garantizar ya que, en términos generales, es imposible hacer ninguna garantía dura en presencia de modificaciones concurrentes no sincronizadas. Las operaciones de falla rápida lanzan ConcurrentModificationException sobre una base de mejor esfuerzo ...

El uso de Iterators también soluciona problemas de concurrencia, como este:

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

Bueno, he intentado todos los aspectos en mi caso donde estaba iterando en una lista de adaptadores, pero debido a los golpes una y otra vez, me mostró el mensaje de excepción que se lanza. Intenté lanzar la lista a

  = (CopyOnWriteArraylist)mylist.value; 

pero también me arrojó una excepción de CouldNotCastException, (y finalmente reflexioné sobre el hecho de que por qué usan o nos proporcionan una fachada de casting).

Incluso utilicé el llamado Bloque Sincronizado también, pero incluso no funcionó o podría haberlo usado de forma incorrecta.

Por lo tanto, es todo cuando finalmente utilicé la # técnica de #todo de tiempo # para manejar la excepción en el bloque try catch, y funcionó. Así que pon tu código en el

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