如何在Android中处理ConcurrentModificationException异常

9

我试图从一个 ArrayList 中删除项目。有时会出现异常,java.util.ConcurrentModificationException

起初我尝试使用 array_list_name.remove(i) 来删除它们,但失败了,有些人建议使用 Iterator。所以我的当前代码如下:

for (Iterator<Collectable> iter = array_list_name.iterator(); iter.hasNext();) {
   Collectable s = iter.next();
   if (s.equals(array_list_name.get(id))){
       iter.remove();
       return true;
   }
}

我在视图中的onDraw()函数中调用了array_list_name。我的视图是一个SurfaceView。有人能建议我如何从ArrayList中删除项目而不会出现此错误吗?


在您的迭代器处于活动状态时,是否有其他线程访问了ArrayList? - Leonard Brünings
onDraw() 运行在一个单独的线程上,而项目的移除则发生在另一个线程上。没有使用其他线程。 - dinesh707
但是 onDraw() 方法可以同时被调用,对吗? - Kai
1
是的,onDraw 在 UI 线程中调用,因此它可能会在另一个线程中的项目删除同时被调用。 - Xion
1
那就是问题所在,参见API如果在迭代进行中修改了底层集合,则迭代器的行为是未指定的... - Kai
显示剩余2条评论
4个回答

10

建议使用 java.util.concurrent.CopyOnWriteArrayList 替换 ArrayList


我只会建议在修改较少的情况下使用,否则手动复制会更好。 - Leonard Brünings

7

从评论中看来,您的ArrayList<Collectable>在一个线程中由UI从onDraw()方法访问,并与您在另一个线程中删除其中项并发访问。

所以,为什么不将两个访问者都包装在一个

synchronized(array_list_name)
{
    // UI access code or item removal code
}

请注意,如果删除项目需要很长时间,则可能会使您的UI变得卡顿。如果是这种情况,请考虑制作一个所有要删除的项目索引列表,并在遍历整个列表后,在一个紧密同步的循环中删除它们。
更新
我认为您的整个代码片段可以简化为:
synchronized(array_list_name)
    return array_list_name.remove(id);

1

您是否考虑过使用Vector List?如果需要线程安全的实现,应该使用Vector而不是ArrayListVector列表的用法与ArrayList相同,只需将其类型更改为Vector

不安全的用法

ArrayList<FutureTask> futureTasks;

Change with

Vector<FutureTask> futureTasks;

就是这样。


1
这绝对是该帖子中最好的答案。使用旨在并发使用的类作为Vector是正确的解决方案。我已经在自己的项目中使用过它,当时我遇到了类似的问题,并且一直收到ConcurrentModificationException的错误提示。非常感谢! - xarlymg89

0
您可以这样创建列表的防御性副本:
List copy = new ArrayList(array_list_name);
for (Iterator<Collectable> iter = copy.iterator(); iter.hasNext();) {
   Collectable s = iter.next();
   if (s.equals(copy.get(id))){
       iter.remove();
       return true;
   }
}

2
但是这会从copy中删除该元素。提问者可能希望从array_list_name中删除该元素。如果您按索引删除它,则无法确定是否获取了正确的元素,因为列表可能已更改。 - Kai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接