Могу ли я использовать много списков для последовательного изменения или удаления элементов списка из ArrayList в Java? - PullRequest
0 голосов
/ 04 ноября 2019

Я полагаюсь на итераторы списка для перемещения по списку символов. Это однопоточная программа, и я использую объекты listIterator последовательно в 4 разных методах. Каждый метод имеет одинаковую настройку:

private void myMethod(ArrayList<Integer> input) {
    ListIterator<Integer> i = input.listIterator();
    while (i.hasNext()) {
        Integer in = i.next();
        if (in < 10)
            i.remove();
        else
            i.set(in*in); // because its lucky
    }
}

С этим шаблоном на втором итераторе выдается следующее исключение:

java.util.ConcurrentModificationException

Однако, глядя на javadocs, я не вижуэто исключение в исключениях выдается, и я не вижу метод, чтобы закрыть итератор после того, как я закончил. Я неправильно использую listIterator? Я должен перебирать один и тот же ArrayList несколько раз, каждый раз, когда условно удаляя или изменяя каждый элемент. Может быть, есть лучший способ перебора ArrayList, и этот вариант использования лучше всего не решить с помощью ListIterator.

документы Java для ListIterator

Ответы [ 3 ]

2 голосов
/ 04 ноября 2019

Это объясняется в ArrayList javadoc, вы изменяете список с помощью remove() и set() при использовании Iterator:

Итераторыметоды, возвращаемые iterator и listIterator этого класса, не подвержены сбоям: , если список структурно изменен в любое время после создания итератора, любым способом, кроме использования собственных методов удаления или добавления итератора, итераторбросит ConcurrentModificationException. Таким образом, перед одновременной модификацией итератор быстро и чисто дает сбой, вместо того, чтобы рисковать произвольным недетерминированным поведением в неопределенное время в будущем.

1 голос
/ 04 ноября 2019

Трудно дать диагностику для проблемы, когда показанный код явно не тот код, который породил исключение, поскольку он даже не компилируется. Метод remove для Iterator не принимает аргументов, а метод set определен для ListIterator, но ваш код объявляет переменную i только как Iterator.

ИсправленоВерсия

private void myMethod(ArrayList<Integer> input) {
    ListIterator<Integer> i = input.listIterator();
    while (i.hasNext()) {
        Integer in = i.next();
        if (in < 10)
            i.remove();
        else
            i.set(in*in);
    }
}

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

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

Тем не менее, вместо этого проще использовать

private void myMethod(ArrayList<Integer> input) {
    input.removeIf(in -> in < 10);
    input.replaceAll(in -> in*in);
}

. В отличие от исходного кода, он выполняет две итерации, но, как объяснено в , этот ответ , removeIf будет на самом деле быстрее, чем удаление на основе итераторов в тех случаях, когда производительность действительно имеет значение.

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

0 голосов
/ 04 ноября 2019

Я недостаточно осведомлен о Java ListIterators, чтобы ответить на вопрос, но, похоже, я столкнулся с проблемой XY здесь. Похоже, проблему лучше решить с помощью Java Streams, чтобы удалить элемент или отобразить элемент в новый ArrayList, выполнив функцию для каждого элемента в исходном ArrayList.

    private ArrayList<Integer> myMethod(ArrayList<Integer> input) {
        ArrayList<Integer> results = input.stream().filter(
            in -> (in < 10)).collect(Collectors.toCollection(ArrayList::new));

        results = input.stream().map(
            in -> in*in).collect(Collectors.toCollection(ArrayList::new));

        return results;
    }
...