[英]Android: Hashmap concurrent Modification Exception
我的代碼不斷出現並發修改異常。 我只是遍歷哈希圖並修改值。 通過研究,我發現人們說要使用iterators和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: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。
正如您將了解如何使用我的選擇。 您可以學習如何使用迭代器 , 如何循環使用迭代器 。 (而不是簡單地復制粘貼)
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>);
}
如果您不刪除任何條目,而只是更改值,這應該對您有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.