2

I keep getting a concurrent modification exception on my code. I'm simply iterating through a hashmap and modifying values. From researching this I found people said to use iterators and iterator.remove, etc. I tried implementing with this and still kept getting the error. I thought maybe multiple threads accessed it? (Although in my code this block is only run in one thread) So I put it in a synchronized block. However, I'm still getting the error.....

 Map map= Collections.synchronizedMap(questionNumberAnswerCache);
        synchronized (map) {
            for (Iterator<Map.Entry<String, Integer>> it = questionNumberAnswerCache.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry<String, Integer> entry = it.next();
                if (entry.getKey() == null || entry.getValue() == null) {
                    continue;
                } else {
                    try {
                        Question me = Question.getQuery().get(entry.getKey());
                        int i = Activity.getQuery()
                                .whereGreaterThan(Constants.kQollegeActivityCreatedAtKey, lastUpdated.get("AnswerNumberCache " + entry.getKey()))
                                .whereEqualTo(Constants.kQollegeActivityTypeKey, Constants.kQollegeActivityTypeAnswer)
                                .whereEqualTo(Constants.kQollegeActivityQuestionKey, me)
                                .find().size();

                        lastUpdated.put("AnswerNumberCache " + entry.getKey(), Calendar.getInstance().getTime());

                        int old_num = entry.getValue();
                        entry.setValue(i + old_num);

                    } catch (ParseException e) {
                        entry.setValue(0);
                    }
                }

            }

        }

Error:

  java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:787)
        at java.util.HashMap$EntryIterator.next(HashMap.java:824)
        at java.util.HashMap$EntryIterator.next(HashMap.java:822)
        at com.juryroom.qollege_android_v1.QollegeCache.refreshQuestionAnswerNumberCache(QollegeCache.java:379)
        at com.juryroom.qollege_android_v1.QollegeCache.refreshQuestionCaches(QollegeCache.java:267)
        at com.juryroom.qollege_android_v1.UpdateCacheService.onHandleIntent(UpdateCacheService.java:28)
        at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.os.HandlerThread.run(HandlerThread.java:61)
3
  • 1
    You're not allowed to modify a collection, while you iterate over it. Commented Sep 18, 2015 at 9:43
  • @PhilippSander Then how does one modify all values in a collection? Make a list of keys, then after I'm outside the iteration, iterate my keys? Commented Sep 18, 2015 at 9:50
  • 1
    Try using ConcurrentHashMap Commented Sep 18, 2015 at 9:57

3 Answers 3

2

What is happening:

The iterator is looping through the map. The map isn't really like a list, because it doesn't care about order. So when you add something to the map it might get inserted into the middle, somewhere in the middle of the objects you already looped through, at the end, etc. So instead of giving you random behavior it fails.

Your solutions:

Synchronized map and synchronized blocks allow you to have two threads going at it at the same time. It doesn't really help here, since the problem is that the same thread is modifying it in an illegal manner.

What you should do:

You could just save the keys you want to modify. Making a map with keys and new values won't be a problem unless this is a really time critical piece of code.

Then you just iterate through the newValues map and update the oldValues map. Since you are not iterating through the map being updated it's not a problem.

Or you could simply iterate just through the keys (for String s : yourMap) and then look up the values you want to change. Since you are just iterating through the keys you are free to change the values (but you can't remove values).

You could also try to use a ConcurrentHashMap which should allow you to modify it, but the behavior is undefined so this is risky. Just changing values shouldn't lead to problems, but if you add or remove you never know if it will end up being iterated through or not.

Sign up to request clarification or add additional context in comments.

2 Comments

So essentially if my keys are strings I can make an array list of strings that are my keys that I want to modify. Then I can iterate through my keys and modify the hashmaps values?
Yeah. As long as you aren't modifying the Collection you are iterating over it's not a problem. So saving "ToBeModified" and looping thourgh it is a valid strategy.
1

Create an object, and is locked to it - a good way to shoot yourself in the foot.

I recommend the following code to remove the hash map.

HashMap<Key, Object> hashMap = new HashMap<>();
LinkedList<Key> listToRemove = new LinkedList<>();
for(Map.Entry<Key, Object> s : hashMap.entrySet()) {
    if(s.getValue().equals("ToDelete")){
        listToRemove.add(s.getKey());
    }
}
for(Key s : listToRemove) {
    hashMap.remove(s);
}

It's not the most beautiful and fastest option, but it should help you to understand how to work with HashMap.

As you will understand how to work my option. You can learn how to work iterators, how to work iterators in loop. (rather than simply copy-paste)

Iterator it = tokenMap.keySet()) 
while(it.hasNext()) {
    if(/* some condition */) it.remove();
}

Comments

1

I would suggest the following for your use case:

for(Key key : hashMap.keySet()) {
    Object value = hashMap.get(key);
    if(<condition>){
        hashMap.put(key, <new value>); 
}

If you are not deleting any entries and just changing the value, this should work for you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.