Удаление элемента из Set Java ошибка

не могу понять почему прога компилируется но при запуске выдает ошибку

*Генерация коллекии сет [65, 1, 97, 66, 2, 69, 6, 71, 42, 14, 49, 19, 83, 20, 21, 53, 56, 59, 92, 29]

Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1584)
at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1607)
at JavaRushLect8.Collections.main(Collections.java:189)*



public static void main(String[] args) throws InterruptedException {

        Set<Integer> setOfInteger = new HashSet<>();
        randomInsert(setOfInteger, 20, 100);
      
        for (int i : setOfInteger) {
            if(i > 20)
            {
              setOfInteger.remove(i);
            }
        }

Хотя если в блоке условия испоьзовать - System.out.println(i) все работает...


Ответы (3 шт):

Автор решения: Aziz Umarov

Вы удаляет перебирая индекс по возрастанию. Представьте вы удалили 99 и следующий хотите удалить 100 откуда ему взяться если после удаления 99 у вас в сете осталось 99. Удаляют из коллекций индексируя по убыванию. Так чтоб не возращяться к проверенным элементам.

А вернуть индекс не удаляя ничего с чего там должна быть ошибка?

→ Ссылка
Автор решения: Дмитрий

Удалять элементы коллекции в процессе ее перебора нельзя.

Я бы предложил 2 варианта:

Set<Integer> result = setOfInteger.stream().filter(i -> i <= 20).collect(Collectors.toSet());

В этом случае создается новая коллекция result с уже отфильтрованными значениями.

А если вам принципиально нужно удалить элементы из исходной коллекции (это лучше не делать, но все же), то на помощь придет итератор:

Iterator<Integer> iterator = setOfInteger.iterator();
while (iterator.hasNext()) {
    if (iterator.next() > 20) iterator.remove();
}
→ Ссылка
Автор решения: Zhenyria

Немного продолжу ответ Дмитрия.

В Java 8 в классе Collection появился метод removeIf(), который удаляет элемент из коллекции по условию. Под капотом он использует всё тот же Iterator, просто он позволяет вам писать меньше кода. Этот метод вызывается у коллекции (Set в том числе), и в него передаётся предикат. Что такое предикат - читайте про функциональные интерфейсы и лямбды.

Вот пример Дмитрия:

Iterator<Integer> iterator = setOfInteger.iterator();
while (iterator.hasNext()) {
    if (iterator.next() > 20) {
        iterator.remove();
    }
}

А вот как выглядит использование метода removeIf():

setOfInteger.removeIf(num -> num > 20);

По-моему намного приятней.

Ещё Дмитрий писал в ответе:

А если вам принципиально нужно удалить элементы из исходной коллекции (это лучше не делать, но все же)

Я лишь немного скажу об этом. Например, сейчас я решал такую задачу: выгружал из базы данных сущность, которая содержит Set. Так вот, если этот Set пересоздать (удалив при этом элемент), то Hibernate выбросит ошибку, потому что он отслеживает этот Set. Поэтому иногда надо удалять элемент напрямую из коллекции, не пересоздавая её.

→ Ссылка