Почему я получаю исключение ConcurentModificationException при удалении из HashMap? - PullRequest
3 голосов
/ 29 ноября 2010

Я хочу удалить элемент из HashMap, применяя критерии. Рассмотрим этот код:

Set<Foo> set = myMap.keySet();
Iterator<Foo> itr = set.iterator();
while (itr.hasNext())
{
    Foo foo = itr.next();
    if (foo.toString().length() < 3) {
        myMap.remove(foo); //remove the pair if key length is less than 3
    }
}

Итак, я получаю исключение ConcurentModificationException, потому что во время итерации я изменяю HashMap. Что я должен делать? Есть ли другой способ поиска моих критериев и выполнения команды удаления в конце, чтобы я мог избежать этого исключения?

Ответы [ 5 ]

13 голосов
/ 29 ноября 2010

Используйте itr.remove() вместо myMap.remove(o.toString())

4 голосов
/ 21 сентября 2014

Начиная с Java 8, Collection предоставляет removeIf(Predicate<? super E>), который удалит все элементы, для которых данный предикат возвращает true.Пример в вопросе может быть переписан как

myMap.keySet().removeIf(o -> o.toString().length() < 3);

Реализация по умолчанию, предоставляемая Collection, использует итератор и вызывает Iterator.remove, но коллекции могут переопределить это, если они могут обеспечить лучшие реализации.Что еще более важно код, использующий removeIf, является более четким и кратким.

3 голосов
/ 29 ноября 2010

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

Это правило верно для всех коллекций (списки, наборы и т. Д.).

1 голос
/ 29 ноября 2010

Да - itr.remove()

Удаляет из базовой коллекции последний элемент, возвращаемый итератором (необязательная операция).Этот метод может быть вызван только один раз для следующего вызова.

Iterator keySet() расширяет HashIterator, чей remove() метод вызывает HashMap.this.removeEntryForKey(key);

Вы также можете получить entrySet(), если вам нужен и ключ, и значение - его итератор имеет то же свойство.

0 голосов
/ 29 ноября 2010

Чтобы делать то, что вы описываете, мне лично нравится использовать функциональный стиль программирования:

Map<String,Object> map = // obtained somehow;

Map<String,Object> filtered = Maps.filterKeys(map, new Predicate() {
    @Override
    public boolean apply(String input) {
        return input.length() < 3;
    }
});

Фрагмент кода использует библиотеку коллекций Google. Он создает представление исходной карты, беря только те записи, ключи которых соответствуют предоставленному предикату.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...