Почему при удалении элемента из Set с кастомным приведением типа может возникнуть ConcurrentModificationException
Что проверяют
Понимаете ли вы, что fail-fast итератор не имеет отношения к многопоточности, и видите ли связь между хранением объектов в Set и их equals с hashCode. Это вопрос про внутренности коллекций, а не про синтаксис.
Как отвечать
Коллекция держит счётчик структурных изменений modCount. Итератор запоминает его при создании и сверяет в next(). Если между созданием итератора и обходом коллекцию правили мимо него (set.remove(x), add), счётчики расходятся и летит ConcurrentModificationException. Поток при этом один, никакой конкуренции не требуется, слово в названии вводит в заблуждение.
Правильные способы удалить во время обхода:
iterator.remove(), он синхронизирует оба счётчика;removeIfу коллекции;- собрать кандидатов в отдельный список и удалить после цикла;
- в Kotlin
mutableIterator()либоfilter, который возвращает новую коллекцию.
Для настоящего многопоточного доступа берут CopyOnWriteArraySet или ConcurrentHashMap.newKeySet().
Про кастомное приведение стоит сказать отдельно: если у элемента переопределён hashCode и после добавления в Set меняется поле, участвующее в хэше, объект остаётся в старом бакете. contains и remove его не находят, элемент висит в наборе навсегда. Это другой баг, не CME, и путать их нельзя.
Где спотыкаются
Считают исключение гарантией. Проверка сделана по принципу «как получится»: при удалении предпоследнего элемента hasNext() вернёт false, цикл тихо закончится, и повреждённые данные всплывут позже. Полагаться на CME как на защиту нельзя.
Решают, что проблема в потоках, и обкладывают код synchronized или оборачивают в Collections.synchronizedSet. Не помогает: счётчики расходятся внутри одного потока, а синхронизация лечит совсем другую болезнь.
Кладут в Set изменяемые объекты, у которых в hashCode входят изменяемые поля. В Android это обычно модель для адаптера, которую правят на месте вместо создания копии. Поля, участвующие в equals и hashCode, должны быть неизменяемыми.