2

Я продолжаю получать исключение параллельной модификации в моем коде. Я просто повторяю через hashmap и изменяя значения. Исследуя это, я обнаружил, что люди говорили использовать итераторы и iterator.remove и т. Д. Я попытался реализовать с этим и все еще продолжал получать ошибку. Я думал, может быть, несколько потоков обратились к нему? (Хотя в моем коде этот блок запускается только в одном потоке). Поэтому я помещаю его в синхронизированный блок. Тем не менее, я все еще получаю ошибку .....Android: Hashmap concurrent Исключение модификации

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) 
+1

Вам не разрешено изменять коллекцию, пока вы перебираете ее. –

+0

@PhilippSander Затем как можно изменить все значения в коллекции? Составьте список ключей, а затем, после того как я выйду из итерации, итерации моих ключей? – Pseduosance

+1

Попробуйте использовать ConcurrentHashMap – yelliver

ответ

2

Что происходит:

Итератора пробегает по карте. Карта на самом деле не похожа на список, потому что это не заботит порядок. Поэтому, когда вы добавляете что-то на карту, оно может быть вставлено в середину, где-то посередине объектов, которые вы уже зацикливали, в конце и т. Д. Поэтому вместо того, чтобы давать вам случайное поведение, он терпит неудачу.

Ваших решения:

Синхронной карта и синхронизированные блоки позволяют иметь два потока происходит у него в то же время. Здесь это действительно не помогает, поскольку проблема в том, что тот же поток модифицирует его незаконным образом.

Что вы должны сделать:

Вы можете просто сохранить ключи, которые вы хотите изменить. Создание карты с ключами и новыми значениями не будет проблемой, если это не критически важный момент кода.

Затем вы просто перебираете карту newValues ​​и обновляете карту oldValues. Поскольку вы не итерируете через обновляемую карту, это не проблема.

Или вы можете просто перебирать только клавиши (для String s: yourMap), а затем искать значения, которые хотите изменить. Поскольку вы просто повторяете ключи, вы можете изменять значения (но вы не можете удалить значения).

Вы также можете попытаться использовать ConcurrentHashMap, который должен позволить вам изменить его, но поведение не определено, поэтому это опасно. Просто изменение значений не должно приводить к проблемам, но если вы добавляете или удаляете, вы никогда не узнаете, закончится ли оно повторением или нет.

+0

Так что, если мои ключи являются строками, я могу сделать список массивов строк, которые являются моими ключами, которые я хочу изменить. Затем я могу выполнять итерацию через мои ключи и изменять значения хешмапов? – Pseduosance

+0

Да. Пока вы не изменяете коллекцию, вы повторяете ее, это не проблема. Таким образом, сохранение «ToBeModified» и циклический контроль правильной стратегии. – Astrogat

1

Создайте объект и заблокируйте его - хороший способ стрелять в ногу.

Я рекомендую следующий код для удаления хэш-карты.

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

Я хотел бы предложить следующее для случая использования:

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

Если вы не удалить какие-либо записи и просто изменить значение, это должно работать для вас ,

Смежные вопросы