简体   繁体   English

Android:Hashmap并发修改异常

[英]Android: Hashmap concurrent Modification Exception

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. 通过研究,我发现人们说要使用iterators和iterator.remove等。我尝试使用它进行实现,但仍然不断出错。 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)

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. 然后,您只需遍历newValues映射并更新oldValues映射。 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. 或者,您可以仅通过键(对于String:yourMap)进行迭代,然后查找要更改的值。 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. 您也可以尝试使用ConcurrentHashMap,它应允许您对其进行修改,但是行为未定义,因此存在风险。 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. 仅更改值不会导致问题,但是如果添加或删除,则永远不会知道它是否最终会被迭代。

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. 它不是最美丽,最快的选择,但它应该可以帮助您了解如何使用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();
}

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. 如果您不删除任何条目,而只是更改值,这应该对您有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM