Исключение ConcurrentModification с использованием addAll поверх Set в Java - PullRequest
0 голосов
/ 12 октября 2010

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

Когда я пытаюсь запустить приведенный ниже код, Java вызывает исключение ConcurrentModificationException в строке с надписью «if (this.vertexStore.get (v) .addAll (output))».

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

Фактически, единственное место, где я могу увидеть возникшую ошибку, - это метод addAll, но этого не должно происходить, так как я использую HashMap и LinkedList из библиотеки классов Java ...

private Queue<Vertex> worklist = new LinkedList<Vertex> ( );
protected Map<Vertex, Set<T>> vertexStore = new HashMap<Vertex, Set<T>> ( );

// . . .

while ( this.worklist.size ( ) > 0 ) {
        Vertex vertex = this.worklist.remove ( );

        Set<T> output = this.processVertice ( vertex, this.vertexStore.get ( vertex ) );

        this.vertexStore.put ( vertex, output );

        for ( Vertex v : vertex.edgesTo ( ) ) {
            // Conveniently, addAll returns true if the set changed
            if ( this.vertexStore.get ( v ).addAll ( output ) )
                this.worklist.add ( v );
        }
    }

РЕДАКТИРОВАТЬ: Ошибка трассировки:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
    at java.util.HashMap$KeyIterator.next(HashMap.java:828)
    at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
    at DataFlowAnalyser.process(DataFlowAnalyser.java:41) (the if line)

Любые хорошие идеи приветствуются!

PS: полный исходный код здесь (извините за отсутствие комментариев, код не завершен)

Ура, Jon

Ответы [ 2 ]

3 голосов
/ 12 октября 2010

Вы вызываете addAll и передаете ссылку на сам набор. Если вы выполните отладку кода, вы увидите, что this.vertexStore.get (v) возвращает тот же объект, на который ссылается выходная переменная.

Обычно это не будет проблемой для HashSet, потому что addAll на самом деле не будет изменять состояние HashSet, если вы просто добавляете все те же элементы. Однако в этом случае вы изменяете экземпляры HamiltonPath после их добавления в набор, что, в свою очередь, изменяет их хэш-код и заставляет HashSet думать, что добавляемый объект отличается от того, который уже есть.

Вот код, который иллюстрирует проблему лучше, чем моя проза:

List<String> list1 = Arrays.asList("foo");
List<String> list2 = Arrays.asList("bar");
Set<List<String>> set = new HashSet<List<String>>();
set.add(list1);
set.add(list2);
list1.add("baz"); 
list2.add("qux");
set.addAll(set); // throws ConcurrentModificationException
0 голосов
/ 12 октября 2010

ваш код ломается, потому что вы изменяете коллекцию, пока выполняете ее, и java это не нравится. Если вы хотите сделать это, используйте ConcurrentHashMap, который использует итератор другого типа, чем стандартная карта.

...