итерация по отсортированной карте дерева: java.util.ConcurrentModificationException

У меня есть базовые знания Java, и в настоящее время я работаю над кодом на основе Java.

РЕДАКТИРОВАТЬ: я не писал код

Я перебираю отсортированную древовидную карту Event объектов и получаю это исключение, когда пытаюсь получить следующий элемент:

java.util.ConcurrentModificationException at java.util.TreeMap$PrivateEntryIterator.nextEntry(Unknown Source) at java.util.TreeMap$KeyIterator.next(Unknown Source)

Я думаю, это должно быть связано с существованием нескольких записей с одинаковым значением, которые объединены компаратором итератора (имеется в виду это вопрос, но я не знаю, как найти ключи, используемые в компараторе. Объект Event имеет много параметров (например, идентификатор, время и т. д.), но не уверен какой из них используется для итератора.

Вот соответствующая часть кода (исключение во втором SimEvent first = fit.next();):

    if (future.size() > 0) {
        List<SimEvent> toRemove = new ArrayList<SimEvent>();
        Iterator<SimEvent> fit = future.iterator();
        queue_empty = false;
        SimEvent first = fit.next();
        processEvent(first);
        future.remove(first);

        fit = future.iterator();

        // Check if next events are at same time...
        boolean trymore = fit.hasNext();
        while (trymore) {
            SimEvent next = fit.next();
            if (next.eventTime() == first.eventTime()) {
                processEvent(next);
                toRemove.add(next);
                trymore = fit.hasNext();
            } else {
                trymore = false;
            }
        }

        future.removeAll(toRemove);

    } else {...}

РЕДАКТИРОВАТЬ: Код зала класса future:

public class FutureQueue {

    /** The sorted set. */
    private final SortedSet<SimEvent> sortedSet = new TreeSet<SimEvent>();

    /** The serial. */
    private long serial = 0;

    /**
     * Add a new event to the queue. Adding a new event to the queue preserves the temporal order of
     * the events in the queue.
         * 
     * @param newEvent The event to be put in the queue.
     */
    public void addEvent(SimEvent newEvent) {
        newEvent.setSerial(serial++);
        sortedSet.add(newEvent);
    }

    /**
    * Add a new event to the head of the queue.
    * 
    * @param newEvent The event to be put in the queue.
    */
    public void addEventFirst(SimEvent newEvent) {
        newEvent.setSerial(0);
        sortedSet.add(newEvent);
    }

    /**
     * Returns an iterator to the queue.
     * 
     * @return the iterator
     */
     public Iterator<SimEvent> iterator() {
        return sortedSet.iterator();
     }

    /**
     * Returns the size of this event queue.
     * 
     * @return the size
     */
    public int size() {
        return sortedSet.size();
    }

    /**
     * Removes the event from the queue.
     * 
     * @param event the event
     * @return true, if successful
     */
    public boolean remove(SimEvent event) {
        return sortedSet.remove(event);
    }

    /**
     * Removes all the events from the queue.
     * 
     * @param events the events
     * @return true, if successful
     */
    public boolean removeAll(Collection<SimEvent> events) {
        return sortedSet.removeAll(events);
    }


    public void clear() {
        sortedSet.clear();
    }
}

Любое предложение о том, как приступить к отладке этой проблемы?


person Betty    schedule 07.06.2017    source источник
comment
Не могли бы вы поделиться кодом класса future?   -  person Vasiliy Vlasov    schedule 07.06.2017
comment
почему вы иногда используете fit.next() без проверки fit.hasNext()?   -  person user7294900    schedule 07.06.2017
comment
@VasiliyVlasov: сделано в редактировании вопроса   -  person Betty    schedule 07.06.2017
comment
user7294900: Я получаю этот код таким образом. Я проверил, что у итератора все еще есть следующий элемент, прежде чем я получаю исключение.   -  person Betty    schedule 07.06.2017
comment
Неясно, получаете ли вы java.util.ConcurrentModificationException (в вопросе) или java.util.NoSuchElementException (в заголовке).   -  person vempo    schedule 07.06.2017
comment
@vempo: ты прав, я сделал ошибку, скопировав свое исключение...   -  person Betty    schedule 07.06.2017


Ответы (1)


ОТРЕДАКТИРОВАНО: Это распространенная ошибка. Вы не можете изменять коллекцию напрямую (добавлять или удалять элементы) при переборе коллекции с помощью Iterator. Удаление, например, должно производиться через сам Iterator.

Правильный способ сделать это выглядит следующим образом (не полный пример, просто для иллюстрации):

Iterator<SimEvent> fit = future.iterator();
while (fit.hasNext()) {
    SimEvent event = fit.next();
    processEvent(event);
    fit.remove();
}

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

person vempo    schedule 07.06.2017
comment
Извините, я не вижу, где используется коллекция (только в методе removeAll класса FutureQueue)? Дело в том, что этот код работает нормально до того, как я добавлю в очередь другие типы событий. - person Betty; 07.06.2017
comment
Вы уверены, что разместили фактический код? Извините за вопрос, но я просматриваю исходный код java.util.TreeMap$PrivateEntryIterator.nextEntry и не вижу другой причины исключения. Вы пытались запустить код в отладчике? - person vempo; 07.06.2017
comment
Да, это. На самом деле я работаю над симулятором, разработанным другими людьми, я добавляю другие типы событий в future в другом месте симулятора, используя метод addEvent. Я запускаю код в отладчике и вижу, что fit имеет следующий элемент, но получаю ошибку. - person Betty; 07.06.2017
comment
Я бы также проверил, правильно ли реализованы equals() и hashCode() SimEvent, и два одинаковых объекта SimEvent всегда имеют один и тот же хеш-код. stackoverflow.com/questions/2265503/ - person vempo; 07.06.2017
comment
Можете ли вы опубликовать код, который инициализирует будущее? Вы где-то добавляете/удаляете из будущего в параллельном потоке? Дело в том, что итератор становится недействительным, если кто-то изменяет объемлющую его коллекцию после того, как итератор был создан. - person vempo; 07.06.2017
comment
Да, я добавляю/изменяю future в другом классе. Для некоторых добавляет, он работает нормально. future в классе Sim объявляется так: protected static FutureQueue future; future = new FutureQueue(); В базовой версии кода события добавляются в future разными методами класса Sim. - person Betty; 07.06.2017
comment
Это может быть проблемой, если вы одновременно (в другом потоке) добавляете событие между future.iterator() и fit.next(), потому что addEvent() модифицирует коллекцию. Это также может объяснить, почему иногда это работает, а иногда нет - это зависит от времени вызова метода. - person vempo; 07.06.2017
comment
Это может быть причиной, но ошибка происходит внутри цикла while(trymore), поэтому это означает, что в то время, когда я назначаю итератор future в fit, даже если future изменяется, это не должно влиять на fit. Изменится ли значение fit, даже если я не обновлю его внутри цикла? - person Betty; 07.06.2017
comment
fit (Итератор) влияет всякий раз, когда модифицируется будущее (TreeSet), из которого оно было получено. См. выше. - person vempo; 07.06.2017
comment
Мне удалось реализовать свои события по-другому: я избегал добавления их напрямую в класс future. Спасибо за ценные комментарии! - person Betty; 19.06.2017