Итерации по коллекции, избегая исключения ConcurrentModificationException при удалении объектов в цикле

Все мы знаем, что из-за ConcurrentModificationException вы не можете делать следующее:

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

Но это, видимо, иногда работает, но не всегда. Вот конкретный код:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

Это, конечно же, приводит к:

Exception in thread "main" java.util.ConcurrentModificationException

Хотя несколько потоков этого не делают. В любом случае.

Как лучше всего решить эту проблему? Как я могу удалить элемент из коллекции в цикле, не вызывая этого исключения?

Я также использую здесь произвольный Collection, не обязательно ArrayList, так что вы не можете полагаться на get.

Примечание для читателей: прочтите docs.oracle.com/javase/tutorial/collections/interfaces/…, там может быть более простой способ достичь того, что вы хотите сделать.

GKFX 05.05.2017 11:38
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1 234
1
491 049
27
Перейти к ответу Данный вопрос помечен как решенный

Ответы 27

Это работает:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

Я предположил, что, поскольку цикл foreach является синтаксическим сахаром для итерации, использование итератора не поможет ... но он дает вам эту функциональность .remove().

цикл foreach является синтаксический сахар для итерации. Однако, как вы указали, вам нужно вызвать remove на итераторе, к которому foreach не дает вам доступа. Отсюда причина, по которой вы не можете удалить в цикле foreach (даже если вы являются фактически используете итератор под капотом)

madlep 22.10.2008 03:30

+1, например, код для использования iter.remove () в контексте, которого в ответе Билла К. [напрямую] нет.

Eddified 03.10.2012 21:01
Ответ принят как подходящий

Iterator.remove() безопасен, вы можете использовать его так:

List<String> list = new ArrayList<>();

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

Обратите внимание, что Iterator.remove() - единственный безопасный способ изменить коллекцию во время итерации; поведение не определено, если базовая коллекция изменена любым другим способом во время итерации.

Источник: docs.oracle> Интерфейс коллекции


Точно так же, если у вас есть ListIterator и вы хотите использовать элементы добавлять, вы можете использовать ListIterator#add по той же причине, по которой вы можете использовать Iterator#remove  - он предназначен для этого.


В вашем случае вы пытались удалить из списка, но то же ограничение применяется при попытке вставить put в Map при повторении его содержимого.

Что, если вы хотите удалить элемент, отличный от элемента, возвращенного в текущей итерации?

Eugen 22.04.2013 13:51

Вы должны использовать .remove в итераторе, и он может удалить только текущий элемент, поэтому нет :)

Bill K 22.04.2013 22:00

Имейте в виду, что это медленнее по сравнению с использованием ConcurrentLinkedDeque или CopyOnWriteArrayList (по крайней мере, в моем случае)

Dan 24.10.2014 05:43

@Josh.У меня отлично работает. Просто использую его сейчас для коллекции объектов WebElement.

user3864935 20.08.2015 15:44

Разве нельзя поместить вызов iterator.next() в цикл for? Если нет, может кто-нибудь объяснить, почему?

Blake 12.04.2016 16:22

Можно поместить еще один вызов iterator.next() в цикл for, но вам также придется добавить еще одну проверку с помощью iterator.hasNext(), чтобы убедиться, что у вас есть еще один элемент для извлечения.

gaoagong 07.07.2016 20:12

Может ли кто-нибудь прокомментировать, насколько хорош этот способ удаления элемента из списка, если в моем списке миллион элементов и есть много возможностей исчерпать пространство кучи java?

Gopal1216 12.07.2017 19:02

Если это связанный список, все должно быть в порядке. Если это список массивов, то у вас проблемы

Bill K 12.07.2017 19:05

Iterator.remove () - необязательная операция, поэтому она не обязательно безопасна (или реализована) для всех итераторов контейнеров.

Gonen I 25.07.2018 19:57

@GonenI Это реализовано для всех итераторов из коллекций, которые не являются неизменяемыми. List.add также является «необязательным» в том же смысле, но вы бы не сказали, что его «небезопасно» добавлять в список.

Radiodef 09.08.2018 03:33

Начиная с Java 8, этот подход встроен как l.removeIf(i -> condition(i));, см. stackoverflow.com/a/29187813/929708

JJ Brown 03.12.2019 01:54

Я использую этот подход и все еще получаю ConcurrentModificationException. Может кто-нибудь объяснить мне, почему это так?

Allix 07.07.2020 18:00

@ bill-k Почему вы проверяете, пуста ли строка? if (string.isEmpty())

Quimbo 14.01.2021 02:52

@Quimbo это просто условие ... показанный пример - удаление пустых строк. Я не писал его, так много людей редактировали этот вопрос - посмотрите историю, чтобы развлечься. Лично мне понравился мой первоначальный ответ, он был похож на две строчки, но я думаю, что важно, чтобы такой популярный ответ был как можно более ясным.

Bill K 16.01.2021 22:26

Вы можете либо использовать итератор напрямую, как вы упомянули, либо сохранить вторую коллекцию и добавить каждый элемент, который вы хотите удалить, в новую коллекцию, а затем удалитьAll в конце. Это позволяет вам продолжать использовать типобезопасность цикла for-each за счет увеличения использования памяти и времени процессора (не должно быть большой проблемой, если у вас нет действительно очень больших списков или действительно старого компьютера)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}

это то, что я обычно делаю, но явный итератор - более элегантное решение, которое я считаю.

Claudiu 22.10.2008 03:51

Достаточно справедливо, если вы больше ничего не делаете с итератором - его раскрытие упрощает выполнение таких вещей, как call .next () дважды за цикл и т. д. Не большая проблема, но может вызвать проблемы, если вы делаете ничего более сложного, чем просто просмотр списка для удаления записей.

RodeoClown 22.10.2008 03:58

@RodeoClown: в исходном вопросе Клаудиу удаляет из Коллекции, а не итератор.

matt b 22.10.2008 18:29

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

RodeoClown 22.10.2008 22:35

Если это простые значения удаления, которые не нужны, и цикл выполняет только это одно, использование итератора напрямую и вызов .remove () абсолютно нормальны.

RodeoClown 22.10.2008 22:36

использование Iterator.remove () работает только с текущим значением, предоставленным итератором. Если у вас есть какое-то условие в вашем цикле, которое требует удаления некоторого члена коллекции Другие, то решение RodeoClown - это то, что вам нужно.

PMorganCA 16.05.2013 18:46

Поскольку на вопрос уже дан ответ, т.е. лучший способ - использовать метод удаления объекта итератора, я бы подробно остановился на том месте, где возникает ошибка "java.util.ConcurrentModificationException".

Каждый класс коллекции имеет частный класс, который реализует интерфейс Iterator и предоставляет такие методы, как next(), remove() и hasNext().

Код для next выглядит примерно так ...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

Здесь метод checkForComodification реализован как

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

Итак, как видите, если вы явно попытаетесь удалить элемент из коллекции. Это приводит к тому, что modCount отличается от expectedModCount, что приводит к исключению ConcurrentModificationException.

Очень интересно. Спасибо! Я часто сам не вызываю remove (), вместо этого я предпочитаю очищать коллекцию после ее итерации. Не сказать, что это хороший образец, просто то, чем я занимаюсь в последнее время.

James T Snell 23.09.2015 21:27

Сделайте копию существующего списка и перебирайте новую копию.

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}

Создание копии звучит как пустая трата ресурсов.

Antzi 21.08.2013 16:35

@Antzi Это зависит от размера списка и плотности объектов внутри. Тем не менее ценное и действенное решение.

mre 04.05.2016 17:14

Я использовал этот метод. Это требует немного больше ресурсов, но гораздо более гибкое и понятное.

Tao Zhang 08.02.2019 02:34

Это хорошее решение, если вы не собираетесь удалять объекты внутри самого цикла, но они скорее «случайным образом» удаляются из других потоков (например, сетевые операции, обновляющие данные). Если вы обнаружите, что часто делаете эти копии, существует даже Java-реализация, которая делает именно это: docs.oracle.com/javase/8/docs/api/java/util/concurrent/…

A1m 26.10.2019 06:15

Копирование списка - это то, что они обычно делают со слушателями на Android. Это подходящее решение для небольших списков.

Slion 08.02.2021 19:50

С Коллекции Eclipse будет работать метод removeIf, определенный в MutableCollection:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

С синтаксисом Java 8 Lambda это можно записать следующим образом:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

Вызов Predicates.cast() здесь необходим, потому что метод removeIf по умолчанию был добавлен в интерфейс java.util.Collection в Java 8.

Примечание: Я коммиттер Коллекции Eclipse.

Тот же ответ, что и Клавдий с циклом for:

for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
    Object object = it.next();
    if (test) {
        it.remove();
    }
}

Возможно, это не лучший способ, но для большинства небольших случаев это приемлемо:

"create a second empty-array and add only the ones you want to keep"

I don't remeber where I read this from... for justiness I will make this wiki in hope someone finds it or just to don't earn rep I don't deserve.

У меня есть предложение по проблеме, описанной выше. Нет необходимости в дополнительном списке или в дополнительном времени. Найдите пример, который будет делать то же самое, но по-другому.

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if ( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

Это позволит избежать исключения параллелизма.

В вопросе прямо говорится, что OP не требуется с использованием ArrayList и, следовательно, не может полагаться на get(). В противном случае, вероятно, хороший подход.

kaskelotti 13.04.2014 15:09

(Пояснение ^) OP использует произвольный интерфейс Collection - Collection не включает get. (Хотя интерфейс FWIW List действительно включает "get").

cellepo 20.12.2018 01:31

Я просто добавил здесь отдельный, более подробный ответ также для while-зацикливания List. Но +1 за этот ответ, потому что он был первым.

cellepo 20.12.2018 02:44

В Java 8 вы можете использовать новый метод removeIf. Применительно к вашему примеру:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

Ооооо! Я надеялся, что что-то в Java 8 или 9 может помочь. Мне это все еще кажется довольно многословным, но мне все равно нравится.

James T Snell 23.09.2015 21:25

Рекомендуется ли реализация equals () и в этом случае?

Anmol Gupta 11.12.2015 11:57

кстати removeIf использует петлю Iterator и while. Вы можете увидеть это на java 8 java.util.Collection.java

omerhakanbilici 31.10.2016 17:10

@omerhakanbilici Некоторые реализации, такие как ArrayList, переопределяют его из соображений производительности. Вы имеете в виду только реализацию по умолчанию.

Didier L 31.03.2017 11:33

@AnmolGupta: Нет, equals здесь вообще не используется, поэтому его не нужно внедрять. (Но, конечно, если вы используете equals в своем тесте, он должен быть реализован так, как вы этого хотите.)

Lii 03.01.2019 13:31

Теперь это должен быть принятый ответ, это будет применимо и желательно для большинства людей, которые ищут ответы здесь.

Anupam 19.11.2020 03:05

В таких случаях обычная уловка (была?) - вернуться назад:

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

Тем не менее, я более чем счастлив, что у вас есть лучшие способы в Java 8, например. removeIf или filter в потоках.

Это хороший трюк. Но он не работал бы с неиндексированными коллекциями, такими как наборы, и был бы очень медленным, скажем, со связанными списками.

Claudiu 29.08.2014 20:12

@Claudiu Да, это определенно только для ArrayList или подобных коллекций.

Landei 30.08.2014 19:47

Я использую ArrayList, это отлично сработало, спасибо.

StarSweeper 21.02.2018 09:30

индексы отличные. Если это так часто, почему бы вам не использовать for(int i = l.size(); i-->0;) {?

John 27.04.2018 23:20

В случае ArrayList: удалить (индекс int)- if (индекс - позиция последнего элемента) обходится без System.arraycopy() и не требует на это времени.

время копирования массива увеличивается, если (индекс уменьшается), кстати элементы списка тоже уменьшаются!

лучший эффективный способ удаления - удаление его элементов в порядке убывания: while(list.size()>0)list.remove(list.size()-1); // принимает O (1) while(list.size()>0)list.remove(0); // принимает O (factorial (n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • для индексного цикла: 1090 мсек
  • для индекса desc: 519 msec --- лучший
  • для итератора: 1043 мс

ConcurrentHashMap или ConcurrentLinkedQueue или ConcurrentSkipListMap могут быть другим вариантом, потому что они никогда не вызовут никаких исключений ConcurrentModificationException, даже если вы удалите или добавите элемент.

Да, и обратите внимание, что все они находятся в пакете java.util.concurrent. Некоторыми другими подобными / общими классами вариантов использования из этого пакета являются CopyOnWriteArrayList и CopyOnWriteArraySet [но не ограничиваясь ими].

cellepo 19.11.2018 06:52

На самом деле, я только что узнал, что, хотя эти объекты структуры данных избегатьConcurrentModificationException, их использование в цикле повышенная-for-loop все еще может вызывать проблемы с индексацией (т.е. все еще пропускающие элементы или IndexOutOfBoundsException ...)

cellepo 20.12.2018 04:13
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

Уловка заключается в том, что после удаления элемента из списка вы пропустите внутренний вызов iterator.next (). он все еще работает! Хотя я не предлагаю писать подобный код, это помогает понять его концепцию :-)

Ваше здоровье!

С традиционным циклом for

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}

Ах, так что это действительно просто повышенная-for-loop, который выдает исключение.

cellepo 20.12.2018 03:11

FWIW - тот же код все равно будет работать после модификации для увеличения i++ в защите от петель, а не в теле цикла.

cellepo 20.12.2018 03:11

Исправление ^: То есть если бы приращение i++ не было условным - теперь я понимаю, почему вы делаете это в теле :)

cellepo 20.12.2018 03:23

ListIterator позволяет добавлять или удалять элементы в списке. Предположим, у вас есть список объектов Car:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}

Интересны дополнительные методы в интерфейсе ListIterator (расширение Iterator), особенно его метод previous.

cellepo 20.12.2018 02:58

Люди утверждают, что одно удаление не могу из коллекции повторяется циклом foreach. Я просто хотел указать, что технически неверен, и точно описать (я знаю, что вопрос OP настолько продвинут, что позволяет избежать этого) код, лежащий в основе этого предположения:

for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
    if (obj.isTouched()) {
        untouchedSet.remove(obj);
        touchedSt.add(obj);
        break;  // this is key to avoiding returning to the foreach
    }
}

Дело не в том, что вы не можете удалить итерированный Colletion, а скорее в том, что вы не можете продолжить итерацию, как только вы это сделаете. Следовательно, break в приведенном выше коде.

Приносим извинения, если этот ответ является несколько специализированным вариантом использования и больше подходит для исходного нить, откуда я прибыл, этот ответ отмечен как дубликат (несмотря на то, что этот поток выглядит более тонким) этого и заблокирован.

The Best way(Recommended) is use of java.util.Concurrent package . By using this package you can easily avoid this Exception . refer Modified Code

public static void main(String[] args) {
    Collection<Integer> l = new CopyOnWriteArrayList<Integer>();

    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }

    for (Integer i : l) {
        if (i.intValue() == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

Вы учли удар по производительности? Каждый раз, когда вы «пишете» в эту структуру, ее содержимое копируется в новый объект. Все это плохо сказывается на производительности.

Shankha057 29.07.2020 15:16

Пример модификации поточно-безопасной коллекции:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}

Я знаю, что этот вопрос слишком стар, чтобы касаться Java 8, но для тех, кто использует Java 8, вы можете легко использовать removeIf ():

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);

Я знаю, что этот вопрос предполагает только Collection, а не конкретнее List. Но для тех, кто читает этот вопрос, кто действительно работает со ссылкой на List, вы можете избежать ConcurrentModificationException с помощью цикла while (при изменении в нем) вместо этого, если вы хотите избежать Iterator (либо если вы хотите избежать этого в целом, либо избегать его специально для достижения порядка зацикливания, отличного от сквозной остановки на каждом элементе [который, я считаю, является единственным порядком, который может выполнять сам Iterator]):

* Обновление: см. Комментарии ниже, поясняющие, что аналогичный вариант также возможен с традиционный-for-loop.

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if (list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

В этом коде нет исключения ConcurrentModificationException.

Здесь мы видим, что цикл не начинается в начале и не останавливается на элементе каждый (что, как я считаю, сам Iterator не может сделать).

FWIW мы также видим, что get вызывается на list, что не могло быть сделано, если бы его ссылка была просто Collection (вместо более конкретного типа ListCollection) - интерфейс List включает get, а интерфейс Collection - нет. Если бы не эта разница, то ссылкой list вместо этого мог бы быть Collection [и, следовательно, технически этот ответ был бы прямым ответом, а не косвенным ответом].

Тот же код FWIWW все еще работает после модификации, чтобы запускать с начала с остановки для каждого элемента (точно так же, как порядок Iterator):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if (list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}

Однако это по-прежнему требует очень тщательного расчета индексов для удаления.

OneCricketeer 20.12.2018 02:56

Кроме того, это просто более подробное объяснение этого ответа stackoverflow.com/a/43441822/2308683

OneCricketeer 20.12.2018 02:57

Полезно знать - спасибо! Этот другой ответ помог мне понять, что именно повышенная-for-loop вызовет ConcurrentModificationException, но неттрадиционный-for-loop (который использует другой ответ) - не осознавая этого раньше, вот почему я был мотивирован написать этот ответ (я ошибочно думали, что это были циклы все, которые вызывают исключение).

cellepo 20.12.2018 03:29

Другой способ - использовать копию вашего arrayList только для итерации:

List<Object> l = ...
    
List<Object> iterationList = ImmutableList.copyOf(l);
    
for (Object curr : iterationList) {
    if (condition(curr)) {
        l.remove(curr);
    }
}

Примечание: i - это не index, а объект. Возможно, было бы более уместно назвать его obj.

luckydonald 29.03.2019 15:05

Выше уже предлагалось еще в 2012 году: stackoverflow.com/a/11201224/3969362 Создание копии списка - это то, что они обычно делают со слушателями на Android. Это подходящее решение для небольших списков.

Slion 08.02.2021 19:47

Одним из решений может быть поворот списка и удаление первого элемента, чтобы избежать исключения ConcurrentModificationException или IndexOutOfBoundsException.

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

Попробуйте это (удаляет все элементы в списке, которые равны i):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}

вы также можете использовать Рекурсия

Рекурсия в java - это процесс, в котором метод постоянно вызывает себя. Метод в java, который вызывает сам себя, называется рекурсивным методом.

Теперь вы можете удалить с помощью следующего кода

l.removeIf(current -> current == 5);

Вы можете использовать цикл while.

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if (entry.getKey().equals("test")) {
        iterator.remove();
    } 
}

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