Java ConcurrentModificationException вызывается при попытке перемещения элементов в ArrayLists [duplicate]

Я начал эту проблему в понедельник 2018-06-04. Наши тесты проводятся каждый будний день. Похоже, что единственное, что изменилось, это версия google-chrome (которая была обновлена ​​до текущей). JVM и Selenium были последними версиями на Linux-боксе (Java 1.8.0_151, selenium 3.12.0, google-chrome 67.0.3396.62 и Xvfb перспектива). В частности, добавление аргументов «--no-sandbox» и «--disable-dev-shm-usage» остановило ошибку. Я рассмотрю эти проблемы, чтобы найти дополнительную информацию об эффекте и другие вопросы, как в том, что вызвало обновление google-chrome.

ChromeOptions options = new ChromeOptions();
        ...
        options.addArguments("--no-sandbox");
        options.addArguments("--disable-dev-shm-usage");
1036
задан Raedwald 28 March 2016 в 14:25
поделиться

21 ответ

1472
ответ дан Pragati Singh 25 August 2018 в 10:03
поделиться

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

"создать второй пустой массив и добавить только те, которые вы хотите keep "

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

-3
ответ дан ajax333221 25 August 2018 в 10:03
поделиться

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

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

coll.removeIf(i -> i.intValue() == 5);
157
ответ дан assylias 25 August 2018 в 10:03
поделиться
Collection<Integer> l = new ArrayList<Integer>();//Do the collection thing...

l.removeIf(i -> i == 5);      //iterates through the collection and removes every occurence of 5

Лямбда-выражения и методы сбора в Jdk 8 входят в Handy и добавляют некоторый синтаксический сахар

0
ответ дан Barnabas Ukwuani 25 August 2018 в 10:03
поделиться

С Коллекции Eclipse (ранее коллекции GS ) будет работать метод 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 .

11
ответ дан bruno 25 August 2018 в 10:03
поделиться

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

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

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

321
ответ дан Christian Neverdal 25 August 2018 в 10:03
поделиться

В дополнение к @assylias answer вы также можете использовать новый поток api, если вы используете Java 8:

List<Integer> l = Arrays.asList(4, 5, 6);

static boolean condition(Integer i) {
    return i == 5;
}

static Predicate<Integer> predicate = YourClassName::condition;

l.stream()
    .filter(predicate.negate())
    .forEach(System.out::println);

Если вы инвертируете условие, решение становится еще более кратким, поскольку вам не нужно negate() предикат, что позволяет использовать только ссылку на метод:

List<Integer> l = Arrays.asList(4, 5, 6);

static boolean condition(Integer i) {
    return i != 5;    // <-- condition has been negated
}

l.stream()
    .filter(YourClassName::condition)
    .forEach(System.out::println);

Один из красотой этого является то, что поток лениво оценивается, т. е. операция filter() фактически не оценивается до тех пор, пока она не будет использована терминальной операцией, такой как forEach(). Подробнее об этом можно найти в учебнике для Oracle .

14
ответ дан Community 25 August 2018 в 10:03
поделиться
List<String> strings=new ArrayList<String>(){};

while(strings.size() > 0) {

 String str = strings.remove(0);
}
-1
ответ дан developer747 25 August 2018 в 10:03
поделиться

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

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

Следующий код выглядит примерно так:

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.

40
ответ дан i_am_zero 25 August 2018 в 10:03
поделиться
1
ответ дан jagdish khetre 25 August 2018 в 10:03
поделиться

A 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);
   }
}
1
ответ дан james.garriss 25 August 2018 в 10:03
поделиться
4
ответ дан John 25 August 2018 в 10:03
поделиться

В таких случаях общий трюк (был?) для возврата назад:

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

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

17
ответ дан Landei 25 August 2018 в 10:03
поделиться

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

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++;
      }
6
ответ дан Lluis Felisart 25 August 2018 в 10:03
поделиться

Вы можете перебирать список, используя for-loop, и вам нужно вызвать list.remove (0). Вам нужно с жестким кодом индексировать индексный параметр remove с нулем. См. Также этот ответ :

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

list.add(1);
list.add(2);
list.add(3);
list.add(4);
int list_size = list.size();
for (int i = 0; i < list_size; i++) {
    list.remove(0);
}
-1
ответ дан Markus Pscheidt 25 August 2018 в 10:03
поделиться

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

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

лучший эффективный способ удаления - удаление его элементов в порядке убывания: while(list.size()>0)list.remove(list.size()-1); // принимает O (1) while(list.size()>0)list.remove(0); // принимает O (факториал ( 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 мсек --- лучший
  • для итератора: 1043 мсек
0
ответ дан Nurlan 25 August 2018 в 10:03
поделиться

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

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}
6
ответ дан Priyank Doshi 25 August 2018 в 10:03
поделиться

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

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = 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));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5)
            itemsToRemove.add(i);
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}
22
ответ дан RodeoClown 25 August 2018 в 10:03
поделиться
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

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

Cheers!

0
ответ дан Srinivasan Thoyyeti 25 August 2018 в 10:03
поделиться

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

//"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;
}

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

0
ответ дан svick 25 August 2018 в 10:03
поделиться

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

0
ответ дан Yessy 25 August 2018 в 10:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: