Как избежать ConcurrentModificationException при использовании Iterator?

Сегодня мы рассмотрим способ избежать ConcurrentModificationException при использовании Iterator. Эта ситуация чаще всего возникает в тех случаях, когда с помощью итератора проходят по элементам коллекции и в какой-то момент при вызове iterator.next() будет брошен ConcurrentModificationException. Эта ситуация может произойти как в многопоточной, так и в однопоточной среде.

Давайте рассмотрим это на простом примере:

Результатом выполнения этого кода будет следующее:

Исходя из результатов выполнения, исключение наступает в то время, когда мы называем метод next() нашего итератора.

Хотите знать как Iterator проверяет количество изменений в коллекции? Вот небольшая справка:

В классе AbstractList есть переменная modCount, где хранится количество изменений списка. Это значение используется в каждом следующем вызове метода next() для проверки каких-либо изменений в функции checkForComodification().

А теперь давайте закомментируем часть кода с ошибкой, запустим программу еще раз и увидим следующий результат:

Поскольку мы обновляем существующий элемент в MyMap, его размер не изменился, следовательно мы не получаем ConcurrentModificationException. Обратите внимание, что результат выполнения программы может отличаться в вашей системе, потому что HashMap набор ключей (keyset) не упорядочен как список. Если вы раскомментировать ту часть кода, где мы добавляем новый элемент в HashMap, то это вызовет ConcurrentModificationException.

А теперь практические советы:

Как избежать ConcurrentModificationException в многопоточной среде

  1. Вы можете конвертировать список в массив, и работать с массивом. Этот подход хорошо работает для малого и среднего размера списка, но если список большой, то это будет сильно влиять на производительность.
  2. Вы можете заблокировать список на время обхода элементов, вставив его в синхронизированный блок. Этот подход не рекомендуется, потому что это попросту убьет преимущества многопоточности.
  3. Вы можете использовать классы ConcurrentHashMap и CopyOnWriteArrayListЭто самый эффективный и правильный подход.

Как избежать ConcurrentModificationException в однопоточной среде

В многопоточной среде вы можете использовать метод remove(), чтобы удалить объект. Но в этом случае вы можете удалить только текущий объект, а не любой другой объект из списка.

Давайте рассмотрим на примере

Результат будет такой:

Выводы

  1. Потокобезопасные коллекции позволяют избежать ConcurrentModificationException
  2. В случае CopyOnWriteArrayList, итератор будет работать с начальным списком.
  3. В случае ConcurrentHashMap, поведение не всегда одинаковое.

Результатом выполнения такого кода:

Будет следующее:

Если изменить код выше на такое:

То получим следующее:

Теперь Вы знаете как избежать ConcurrentModificationException при использовании Iterator.

Следите за обновлениями на javadevblog.com.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *