5

In my Android app I use this code while drawing some waypoints on a map

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

But I am getting this error

Fatal Exception: java.util.ConcurrentModificationException java.util.ArrayList$ArrayListIterator.next (ArrayList.java:573)

I am not modifying the list directly in the loop I am iterating over.

But it is possible that I modify the list in another thread because a user can move some waypoints. And the drawing of a waypoint can happen the same time a user uses the touch display to move a waypoint.

Can I avoid that exception somehow?

juergen d
  • 201,996
  • 37
  • 293
  • 362
  • Cant you make `another reference` of the `same list` ? or is it gonna `conflict` ? – Santanu Sur Mar 10 '18 at 12:15
  • It won't work because the underlying list will be the same.. just the reference will be different. – Yogesh Badke Mar 10 '18 at 12:18
  • How often is your code executed correctly and how often the ConcurrentModificationException occurs? If it is e.g. a paint routine that is execute several times a second I would just catch the exception and restart iteration. Because users act typically very slow and don't add dozen points a second. – Robert Mar 10 '18 at 12:22

3 Answers3

9

If you want to maintain a List you use in several threads, it's best you use a concurrent list, such as CopyOnWriteArrayList.

Locally, you can avoid the exception by creating a copy of the waypoints list first and iterate that:

Iterator<Waypoint> iterator = new ArrayList<>(waypoints).iterator();
while (iterator.hasNext()) {
    handle(iterator.next());
}
daniu
  • 14,137
  • 4
  • 32
  • 53
1

The iterator provided by array list is fail-fast iterator - meaning it fails as soon as the underlying list is modified.

One way to avoid the exception is to take a snapshot of the list into another list and then iterate over it.

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

another way is to use collection that implements fail-safe iterators such as CopyOnWriteArrayList.

Yogesh Badke
  • 4,249
  • 2
  • 15
  • 23
  • Copying the array list every time before iterating is costly, especially when the probability that ConcurrentModificationException is very low. – Robert Mar 10 '18 at 12:20
-1

I see some options:

a. Avoid multithreading. Well, you don't have to avoid multithreading completely, just for access to the array. All accesses to the array (even read) must happen from the same thread. Heavy computations can happen on some other threads, of course. This might be a reasonable approach when you can iterate fast.

b. Lock the ArrayList, even for reading. This can be tricky, as excessive locking can introduce deadlocks.

c. Use data copies. Remember, you copy just references, but you usually don't have to clone all the objects. For large data structures, it might be worth considering some persistent data structure, which does not require to copy all the data.

d. Deal with the ConcurrentModificationException somehow. Maybe restart the computation. This might be useful in some cases, but it might get tricky in complex code. Also, in some cases when accessing multiple shared data structures, you might get a livelock – two (or more) threads causing ConcurrentModificationException repeatedly to each other.

EDIT: For some approaches (at least A), you might find reactive programming useful, because this programming style reduces the time spent in the main thread.

v6ak
  • 1,636
  • 2
  • 12
  • 27