Android: Hashmap并发修改异常

Android: Hashmap concurrent Modification Exception

我的代码不断出现并发修改异常。我只是遍历哈希图并修改值。通过研究,我发现有人说要使用迭代器和 iterator.remove 等。我尝试用它来实现,但仍然不断出现错误。我想也许多个线程访问它? (虽然在我的代码中这个块在一个线程中只有运行)所以我把它放在一个同步块中。但是,我仍然收到错误......

 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);
                    }
                }

            }

        }

错误:

  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)

发生了什么:

迭代器正在遍历地图。地图并不像列表,因为它不关心顺序。因此,当您将某些内容添加到地图时,它可能会插入到中间,在您已经循环遍历的对象中间的某个位置,在最后等等。因此,它不会给您随机行为,而是会失败。

您的解决方案:

同步映射和同步块允许您同时处理两个线程。它在这里并没有真正的帮助,因为问题是同一个线程正在以非法方式修改它。

你应该做什么:

您可以只保存要修改的键。使用键和新值制作地图不会有问题,除非这是一段真正时间紧迫的代码。

然后您只需遍历 newValues 映射并更新 oldValues 映射。由于您没有遍历正在更新的地图,所以这不是问题。

或者您可以简单地遍历键(对于 String s : yourMap),然后查找您想要更改的值。由于您只是遍历键,因此您可以自由更改值(但不能删除值)。

您也可以尝试使用 ConcurrentHashMap,它应该允许您对其进行修改,但行为未定义,因此这是有风险的。仅仅改变值不应该导致问题,但是如果你添加或删除你永远不知道它是否最终会被迭代。

创建一个对象,并锁定到它 - 搬起石头砸自己脚的好方法。

我推荐以下代码来删除哈希映射。

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);
}

它不是最漂亮和最快的选择,但它应该可以帮助您了解如何使用 HashMap。

你会明白我的选择是如何运作的。你可以学习how to work iterators, how to work iterators in loop。 (而不是简单地复制粘贴)

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

我会针对您的用例提出以下建议:

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

如果您不删除任何条目而只是更改值,这应该适合您。