Подтвердить что ты не робот

Исключение с одновременной модификацией

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

import java.util.*;

public class SomeClass {
    public static void main(String[] args) {
        List<String> s = new ArrayList<>();
        ListIterator<String> it = s.listIterator();

        for (String a : args)
            s.add(a);

        if (it.hasNext())
            String item = it.next();

        System.out.println(s);
    }
}
4b9b3361

Ответ 1

Чтобы избежать ConcurrentModificationException, вы должны написать свой код следующим образом:

import java.util.*;

public class SomeClass {

    public static void main(String[] args) {
        List<String> s = new ArrayList<String>();

        for(String a : args)
            s.add(a);

        ListIterator<String> it = s.listIterator();    
        if(it.hasNext()) {  
            String item = it.next();   
        }  

        System.out.println(s);

    }
}

A java.util.ListIterator позволяет вам изменять список во время итерации, но не между его созданием и использованием.

Ответ 2

Я не могу понять, почему я продолжаю получать его, хотя я не вижу каких-либо одновременных модификаций.

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

    ListIterator<String> it = s.listIterator();  

    for (String a : args)
        s.add(a);                    // concurrent modification here

    if (it.hasNext())
        String item = it.next();     // exception thrown here

Создайте итератор ПОСЛЕ того, как вы добавили элементы в список:

    for (String a : args)
        s.add(a); 

    ListIterator<String> it = s.listIterator();  
    if (it.hasNext())
        String item = it.next();

Ответ 3

Из JavaDoc: для ConcurrentModificatoinException: "для одного потока обычно не требуется изменять коллекцию, в то время как другой поток выполняет итерацию по ней".

Это просто означает, что если у вас все еще есть открытый итератор, вам не разрешается изменять список, потому что цикл итератора будет прерываться. Попробуйте переместить ListIterator<String> it = s.listIterator(); до цикла for.

Ответ 4

Вам не разрешается продолжить итерацию по итератору после изменения базового списка. Здесь вы создаете итератор перед добавлением нескольких элементов в s, а затем переходите к hasNext() и a next() на нем после добавления, что приводит к ConcurrentModificationException

Ответ 5

Если вышеуказанные решения не работают должным образом. Вы можете использовать старый for-loop для итерации списка одновременно с добавлением новых элементов. См. пример ниже:

import java.util.*;

public class SomeClass {
    public static void main(String[] args) {
        ArrayList<AClass> aList = new ArrayList<AClass>(); // we will iterate this


        // this will cause ConcurrentModificationException. 
        // Since we are iterating the list, at the same time modifying it.
        /*for(AClass a: aList){
           aList.add(someMethod(a));
        }*/

        // old fashion for-loop will help
        int limit = aList.size();
        for(int i=0; ctr<limit; ++i){
           AClass a = aList.get(i);
           aList.add(someMethod(a));
        }


    }
}

Ответ 6

ConcurrentModificationException может возникать как в однопоточной среде, так и в многопоточной среде. Основной улов заключается в том, что все итераторы общего назначения (например, используемые в ArrayList) - это Итераторы FailFast, которые не работают, когда мы пытаемся изменить один список, если один итератор уже выполняет итерацию по нему. Решение → Используйте CopyOnWriteArrayList, если такой сценарий необходим по требованию, а не с использованием ArrayList.

Для полной демонстрации этого можно использовать приведенный ниже код. Нам просто нужно изменить реализацию из CopyOnWriteArrayList в ArrayList.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author narif
 *
 */
public class TestApp {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List<String> testList = new ArrayList<>();
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add("abc");
        testList.add(6, "abcAtindex6");
        int size = testList.size();
        System.out.println("The Current List (ArrayList) is: " + testList);
        System.out.println("The size of the List (ArrayList) is: " + size);
        /* Comment the below lines to get the ConcurrentModificationException */
        testList = new CopyOnWriteArrayList<>(testList);
        for (String value : testList) {
            System.out.println("The Value from ForEach Loop is: " + value);
            /*
             * Concurrent modification is happening here
             * One iterator is iterating over the list while we are trying to add new values to
             * the list so the results of the iteration are undefined under these circumstances.
             * So teh fail fast iterators will fail and will throw the ConcurrentModificationException.
             */
            testList.add("valueFromForLoop");
            testList.add("anotherValueFromForEachLoop");
        }
        Iterator<String> it = testList.iterator();
        while (it.hasNext()) {
            String abc = it.next();
            System.out.println(abc);
            testList.add("Value from Iterator1");
            testList.add("Value from Iterator2");
            testList.add("Value from Iterator3");
            testList.add("Value from Iterator4");

        }
        System.out.println("Did the modificationa and all after conevrting the ArrayList to CopyOnWriteArrayList.");
        System.out.println("Calling the method to get the new List..");
        testList = new CopyOnWriteArrayList<>(getTheList(testList));
        for (String value : testList) {
            System.out.println("The value returned from method is : " + value);
        }
    }

    private static List<String> getTheList(List<String> pList) {
        List<String> list = new CopyOnWriteArrayList<>(pList);
        int i = 0;
        for (String lValue : list) {
            System.out.println("The list Passed is " + list);
            i++;
            list.add("localVaueFromMethod" + i);
            list.removeAll(pList);
        }
        return list;
    }

}

Для более inifo следуйте этой ссылке, это может быть полезно ala ConcurrentModificationException Java Docs

Ответ 7

Посмотрите на страницу оракула .

public class ConcurrentModificationException
extends RuntimeException

Это исключение может быть вызвано методами, которые обнаружили одновременную модификацию объекта, если такая модификация недопустима

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

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

Если вы измените свой код на Stephen C, вы не получите эту ошибку.

Ответ 8

Это не сработало:

LinkedList<String> linkedList = new LinkedList<String>();
ListIterator listIterator = linkedList.listIterator();
linkedList.add("aa");
linkedList.add("bb");

Это сработало:

LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("aa");
linkedList.add("bb");
ListIterator listIterator = linkedList.listIterator();

Ответ 9

чтобы понять это, рассмотрим источник реализации HashMap:

public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable{

который содержит HashIterator, как показано ниже:

private abstract class HashIterator {
    ...
    int expectedModCount = modCount;
    ...

    HashMapEntry<K, V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        .... 
        }

каждый раз, когда вы создаете итератор:

  • создается счетчик expectedModCount и устанавливается значение modCount как контрольная точка входа
  • modCount увеличивается в случаях использования put/get (add/remove)
  • nextEntry метод итератора проверяет это значение с текущим modCount, если они являются разными исключениями одновременной модификации, бросает

, чтобы избежать этого:

  • преобразовать карту в массив (не рекомендуется для больших карт)
  • используйте concurrency классы карт или списков (CopyOnWriteArrayList/ConcurrentMap)
  • заблокировать карту (этот подход удаляет преимущества многопоточности)

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

Concurrency map/list iterator - это "слабо согласованный" итератор, который будет никогда не бросать ConcurrentModificationException и гарантирует пересекающихся элементов, поскольку они существовали при построении итератора, и может (но не гарантируется) отражать любые изменения после строительства.

Дополнительная информация о CopyOnWriteArrayList