user2099024
user2099024

Reputation: 1462

Java ConcurrentModificationException when iterating ArrayList

I get ConcurrentModificationException when iterating an ArrayList and adding objects to a secondary ArrayList. I don't really know why because I'm not editing the list that I'm iterating through.

This happens in two parts of my code. These are the codes.

EDIT - Code 1:

public static ConcurrentHashMap<Long, ArrayList<HistoricalIndex>> historicalIndexesMap = new ConcurrentHashMap<Long, ArrayList<HistoricalIndex>>();

ArrayList<HistoricalIndex> historicalIndexList = IndexService.historicalIndexesMap.get(id);
List<Double> tmpList = new ArrayList<Double>();
for(HistoricalIndex hi : historicalIndexList){ //EXCEPTION HERE
    if((System.currentTimeMillis()-hi.getTimestamp()) >= ONE_MINUTE){
        tmpList.add(hi.getIndex());
    }
}

In Code 1 above, should I copy the historicalIndexList like this:

ArrayList<HistoricalIndex> historicalIndexList = new ArrayList<HistoricalIndex>(IndexService.historicalIndexesMap.get(id));

instead of doing this: ?

ArrayList<HistoricalIndex> historicalIndexList = IndexService.historicalIndexesMap.get(id);

Code 2:

List<Double> tmpList = new ArrayList<Double>();
for(HistoricalIndex hi : list){ //EXCEPTION HERE
    tmpList.add(hi.getIndex());
}

Does anyone have a clue why this happens?

Stacktrace:

21:19:50,426 ERROR [stderr] (pool-9-thread-6) java.util.ConcurrentModificationException
21:19:50,429 ERROR [stderr] (pool-9-thread-6)   at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
21:19:50,432 ERROR [stderr] (pool-9-thread-6)   at java.util.ArrayList$Itr.next(ArrayList.java:831

Upvotes: 2

Views: 3471

Answers (2)

BarrySW19
BarrySW19

Reputation: 3809

Let's take a brief look at what causes a ConcurrentModificationException. The ArrayList maintains internally a modification count value which is just an integer which gets incremented every time there is a modification to the list. When an iterator is created it takes a 'snapshot' of this value. Then, every time the iterator is used it checks that its copy of this value still matches the array's own copy. If it does not it throws the exception.

This means that for a ConcurrentModificationException to occur there must have been some modification to the ArrayList after you first created the iterator (i.e. after the for() statement was first executed but before it ends). As your for() loop is not modifying the ArrayList it means that some other thread must be changing the array while you are iterating through it.

EDIT: In reply to your edit, yes you should copy the array if other threads would be changing it. You could even do something like:

for(HistoricalIndex hi: new ArrayList<HistoricalIndex>(historicalIndexList))

... to copy the list when starting your loop.

To sum up, a ConcurrentModificationException has nothing to do with what we normally think of as concurrency problems. You can quite easily get one in a single thread by modifying an array within an iterator loop other than via the iterator's remove() method. 'Concurrent' in this case means iteration and modification are occurring concurrently - whether in the same or different threads.

Upvotes: 5

Albert
Albert

Reputation: 1216

That's because you are trying to access to it concurrently, and ArrayList it not synchronized.
You can use java.util.Vectorwhich is synchronized or make ArrayListsynchronized doing:

Collections.synchronizedList(new ArrayList(...)); 

As @izca comments, to avoid cocurrent modification, you should put the list created in a synchronized block:

List<T> myList = Collections.synchronizedList(new ArrayList<T>(...)); 
synchronized(myList) { 
    // to modify elements in myList
}

Upvotes: -1

Related Questions