使用Iterator.next()避免ConcurrentModificationException

时间:2018-03-10 12:03:50

标签: java android iterator concurrentmodification

在我的Android应用中,我在地图上绘制一些航点时使用此代码

Iterator<Waypoint> iterator = waypoints.iterator();
while (iterator.hasNext()) {
   Waypoint w = iterator.next();
}

但我收到此错误

  

致命异常:java.util.ConcurrentModificationException   java.util.ArrayList $ ArrayListIterator.next(ArrayList.java:573)

直接在我正在迭代的循环中修改列表。

但我可能会修改另一个线程中的列表,因为用户可以移动一些路点。并且航点的绘制可以在用户使用触摸显示器移动航路点的同时发生。

我可以以某种方式避免这种异常吗?

3 个答案:

答案 0 :(得分:4)

如果您希望维护在多个主题中使用的List,最好使用并发列表,例如CopyOnWriteArrayList

在本地,您可以先通过创建航点列表的副本来避免异常,然后重复:

Iterator<Waypoint> iterator = new ArrayList<>(waypoints).iterator();
while (iterator.hasNext()) {
    handle(iterator.next());
}

答案 1 :(得分:0)

数组列表提供的迭代器是fail-fast迭代器 - 意味着一旦基础列表被修改就会失败。

避免异常的一种方法是将列表的快照写入另一个列表,然后迭代它。

Iterator<Waypoint> iterator = new ArrayList<>(waypoints).iterator();
while (iterator.hasNext()) {
   Waypoint w = iterator.next();
}

另一种方法是使用实​​现故障安全迭代器的集合,例如CopyOnWriteArrayList

答案 2 :(得分:0)

我看到了一些选择:

一个。避免多线程。好吧,你不必完全避免多线程,只是为了访问阵列。对阵列的所有访问(甚至是读取)都必须来自同一个线程。当然,在其他一些线程上可能会发生繁重的计算。当您可以快速迭代时,这可能是一种合理的方法。

湾即使是为了阅读,也要锁定ArrayList。这可能很棘手,因为过度锁定会导致死锁。

℃。使用数据副本。请记住,您只复制引用,但通常不必克隆所有对象。对于大型数据结构,可能值得考虑一些persistent data structure,这不需要复制所有数据。

d。以某种方式处理ConcurrentModificationException。也许重启计算。这在某些情况下可能很有用,但在复杂的代码中可能会变得棘手。此外,在某些情况下,在访问多个共享数据结构时,您可能会遇到一个活锁 - 两个(或更多)线程导致彼此重复出现ConcurrentModificationException。

编辑:对于某些方法(至少是A),您可能会发现反应式编程很有用,因为这种编程风格减少了在主线程中花费的时间。