ConcurrentModificationException и Хешмап

Помимо команд LoCo, вы также можете попробовать свои локальные LUG (Linux User Group) или общий компьютерный клуб (у них может быть рабочая группа Linux или Ubuntu).

1
задан 2 June 2018 в 15:30

4 ответа

Создал небольшую мапу:

final HashMap<Object, Object> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");

Следующие выражения выбьют ошибку:

Java 8+:

map.entrySet().stream().filter((i) -> ("3".equals(i.getKey()))).forEachOrdered((i) -> {
    map.remove(i.getKey());
});

Java 8+:

for (Entry<Object, Object> i : map.entrySet()) {
    if ("2".equals(i.getKey())) {
         map.remove(i.getKey());
    }
}

Выбражения, которые не выдадут ошибки: Работает только на 1 удаление, если добавить еще один if для удаления, выдаст ошибку NoSuchElementException

final Iterator<Entry<Object, Object>> i = map.entrySet().iterator();
while (i.hasNext()) {
    if ("2".equals(i.next().getKey())) {
       i.remove();
    }
}

Для удаления, добавления, изменения, лучше использовать switch:

final Map<Object, Object> map = new ConcurrentHashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "5");
final Iterator<Entry<Object, Object>> i = map.entrySet().iterator();
while (i.hasNext()) {
    switch ((String) i.next().getKey()) {
        case "3":
            i.remove();
            map.put("5", "5");
            final Iterator<Entry<Object, Object>> it = map.entrySet().iterator();
            while (it.hasNext()) {
                if ("2".equals((String) it.next().getKey())) {
                    it.remove();
                    map.put("5", "5");
                    map.put("6", "6");
                    map.remove(map.get("1"));

                }
            }
        break;
    }
}
System.out.println(map); // {4=5, 5=5, 6=6}

Но опять-же Если в хранилище залетел какой-то объект, во время:

while (i.hasNext()) {
    switch((String)i.next().getKey()) {
       //.....
    }
}

Получим ConcurrentModificationException.

synchronized(map) {
   //....
}

Тогда можно будет не бояться, что залетает.

1
ответ дан 6 June 2018 в 11:59
  • 1
    Исключение ConcurrentModification не связано с многопоточностью и synchronized не поможет. – Hivemaster 31 May 2018 в 09:54
  • 2
    @Hivemaster, синхронизацию использывать на мапы: ConcurrentHashMap, HashMap, так как напрямую можно работать из Collections.synchronizedMap к примеру. И чтобы при добавлении удаления, пока не будет доступен, не выполнится. – And 31 May 2018 в 10:43
  • 3
    Это имеет смысл в многопоточном коде, но как синхронизация поможет избежать ConcurrentModificationException в однопоточном, как у автора вопроса? Я не понимаю, чем вообще ваш ответ может помочь. – Hivemaster 31 May 2018 в 10:47
  • 4
    @Hivemaster раз автор отметил его решить, значит чем-то помог. – Sergey Gornostaev 31 May 2018 в 10:50
  • 5
    @SergeyGornostaev это здорово, но было бы еще неплохо, если бы другие пользователи с таким же проблемой тоже смогли воспользоваться ответом. – Hivemaster 31 May 2018 в 10:51

Вместо цикла foreach используйте явный итератор и его метод remove.

1
ответ дан 6 June 2018 в 11:59
  • 1
    removeIf я это и делал. – Санаев 31 May 2018 в 08:37
  • 2
    начиная с 8 явы можно использовать removeIf – Санаев 31 May 2018 в 08:38
  • 3
    Iterator.remove() и Collection.removeIf() - это не одно и то же. – Sergey Gornostaev 31 May 2018 в 08:38
  • 4
    Сейчас попробую исправить. – Санаев 31 May 2018 в 08:39
  • 5
    пишу так: for (Iterator & lt; Map.Entry & lt; String, Integer & gt; it = freq.entrySet (). iterator (); it.hasNext ();) {if (it.next (). getKey (). equals (s)) {it.remove (); }} идея говорит - меняй на removeIf – Санаев 31 May 2018 в 08:43

Подсказали решение: Изменить for (Transaction t : c.getTransactions()) на

for (int i=0;i<clusters.size();i++) { 
 Cluster c = clusters.get(i); 
 List<Transaction> transactions = new ArrayList<>(c.getTransactions()); 
 for (Transaction t : transactions) { 
... 
} 
} 
0
ответ дан 6 June 2018 в 11:59

Foreach - это механизм для работы с элементами коллекции, а не с самой коллекцией. Использование foreach для модификации коллекции - это уже не правильно. А писать костыли и велосипеды, чтобы «расширить возможности» конструкций языка - и совсем плохая затея.

Вот один из классических подходов работы с итераторами:

for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
  if (it.next() % 2 == 0) {
    it.remove();
  }
}

]

Взято отсюда:

https://habr.com/post/325426/#comment_10149968

-3
ответ дан 6 June 2018 в 11:59

Другие вопросы по тегам:

Похожие вопросы: