Iterator не поднимает concurrentModificationException, когда мы удаляем последний элемент из списка массивов при повторении [duplicate]

У меня также была проблема с OSX. Решение было раскомментировано extension = pgsql.so в php.ini.default и удалением суффикса .default, так как файла php.ini не было.

Если вы используете XAMPP, файл php.ini находится в / XAMPP / xampfiles / etc

17
задан Kevin J. Chase 25 November 2015 в 12:03
поделиться

5 ответов

Короткий ответ

Поскольку отказоустойчивое поведение итератора не гарантируется.

Длинный ответ

Вы получаете это исключение, потому что вы

Плохо:

// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {  
    // here, the collection will check it hasn't been modified (in effort to fail fast)
    String s = i.next();
    if(s.equals("lalala")) {
        // s is removed from the collection and the collection will take note it was modified
        c.remove(s);
    }
}

Хорошо:

// we're using iterator
for (Iterator<String> i = c.iterator(); i.hasNext();) {  
    // here, the collection will check it hasn't been modified (in effort to fail fast)
    String s = i.next();
    if(s.equals("lalala")) {
        // s is removed from the collection through iterator, so the iterator knows the collection changed and can resume the iteration
        i.remove();
    }
}

Теперь, чтобы перейти к следующему элементу, «почему»: в приведенном выше коде обратите внимание на то, как выполняется проверка модификации - удаление помещает коллекцию как измененную, а следующая итерация проверяет любые изменения и терпит неудачу, если она обнаруживает изменение коллекции. Еще одна важная вещь: ArrayList (не уверенный в других сборниках) делает not проверкой на модификацию в hasNext().

Поэтому могут произойти две странные вещи:

  • Если вы удалите последний элемент во время итерации, ничего не будет выбрано. Это потому, что нет «следующего» элемента, поэтому итерация заканчивается до достижения кода проверки модификации
  • . Если вы удалите второй-последний элемент ArrayList.hasNext() будет также возвращать false, потому что итератор current index теперь указывает на последний элемент (бывший второй по-последнему). Таким образом, даже в этом случае после удаления нет «следующего» элемента

Обратите внимание, что все это соответствует документации ArrayList :

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

Отредактировано для добавления:

Этот вопрос дает некоторую информацию о том, почему проверка параллельной модификации не выполнена в hasNext() и выполняется только в next().

]
22
ответ дан Community 16 August 2018 в 12:10
поделиться
  • 1
    в обоих случаях функция удаления такая же. Мой вопрос в том, почему он не дает ошибку во втором случае, но в первом случае он дает ошибку? – Ravi Kuldeep 25 November 2015 в 11:26
  • 2
    На самом деле, не имеет значения, какой элемент вы удаляете, это имеет значение только для текущего элемента, то есть там, где расположен итератор. Любая позиция, отличная от второй, будет генерировать исключение, но если итератор находится во втором-последнем элементе и вы удаляете элемент any , итерация останавливается без ошибок и без повторения последнего элемента. – Andreas 25 November 2015 в 11:41
  • 3
    но в тот момент, когда коллекция будет изменена, не следует ли исключать в тот момент? Я не знаю код позади hasnext (), next () и remove (Object o). Можете ли вы, пожалуйста, объяснить мне в отношении реализации этих кодов. – Ravi Kuldeep 25 November 2015 в 11:47
  • 4
    @ Андреас, ты прав. Обычная ошибка заключается в попытке удалить "текущий" элемент через коллекцию, а не через итератор, поэтому я буду придерживаться этого. – Jiri Tousek 25 November 2015 в 11:48
  • 5
    @RaviKuldeep невозможно создать исключение, когда коллекция будет изменена, потому что коллекция не знает о существующих итераторах (она не может знать, какие итераторы все еще «живы», даже если она отслеживает созданные итераторы), поэтому единственное возможной точкой для проверки на модификацию является обращение к итератору. Можно проверить Iterator.hasNext(), но Java почему-то не делает этого. – Jiri Tousek 25 November 2015 в 12:22

Если вы посмотрите на исходный код для итератора ArrayList (частный вложенный класс Itr), вы увидите недостаток в коде.

Код должен быть с ошибкой, быстрый, который делается внутри итератора внутри, вызывая checkForComodification(), однако hasNext() не делает этот вызов, вероятно, по соображениям производительности.

Вместо hasNext():

public boolean hasNext() {
    return cursor != size;
}

Это означает, что когда вы находитесь в элементе second last в списке, а затем удалите элемент (любой элемент), размер уменьшается, а hasNext() думает, что вы 're на последнем элементе (которого вы не были) и возвращает false, пропуская итерацию последнего элемента без ошибок.

OOPS !!!!

4
ответ дан Andreas 16 August 2018 в 12:10
поделиться
  • 1
    Я проверил реализации hasnext (), next () и remove (Object o), но не смог их полностью понять. Не могли бы вы объяснить ответ в отношении этих реализаций? – Ravi Kuldeep 25 November 2015 в 11:52
  • 2
    @RaviKuldeep Я показал реализацию hasNext() и объяснил, как / почему он терпит неудачу. Я не собираюсь подробно объяснять, как работает весь класс итератора. Если вы не можете понять код, просто примите тот факт, что он работает при правильном использовании и не гарантирует сбой при неправильном использовании. Позже, когда вы лучше понимаете Java, вы можете снова взглянуть на нее. – Andreas 25 November 2015 в 11:56

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

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at com.ii4sm.controller.Evil.removeLalala(Evil.java:23)
    at com.ii4sm.controller.Evil.main(Evil.java:17)

В stacktrace очевидно, что строка i.next(); выдает ошибку. Но когда у вас есть только два элемента в коллекции.

Collection<String> c = new ArrayList<String>();
c.add("lalala");
c.add("lalala");
removeLalala(c);
System.err.println(c);

Когда первый удаляется i.hasNext() возвращает false, а i.next() никогда не выполняется, чтобы выбросить исключение

3
ответ дан awsome 16 August 2018 в 12:10
поделиться

вы должны удалить из iterator (i), а не collection (c) напрямую;

попробуйте это:

for (Iterator<String> i = c.iterator(); i.hasNext();) {
    String s = i.next();
    if(s.equals("lalala")) {
        i.remove(); //note here, changing c to  i with no parameter.
    }
}

EDIT:

Причина, по которой первая попытка вызывает исключение, а вторая - нет, просто из-за количества элементов в вашей коллекции.

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

2
ответ дан nafas 16 August 2018 в 12:10
поделиться
  • 1
    в обоих случаях функция удаления такая же. Мой вопрос в том, почему он не дает ошибку во втором случае, но в первом случае он дает ошибку? – Ravi Kuldeep 25 November 2015 в 11:24
  • 2
    @RaviKuldeep Я обновил свой ответ – nafas 25 November 2015 в 11:29
  • 3
    но в тот момент, когда коллекция будет изменена, не следует ли исключать в тот момент? Я не знаю код позади hasnext (), next () и remove (Object o). Можете ли вы мне рассказать о реализации этих кодов. Исправьте меня, если я понимаю это неправильно – Ravi Kuldeep 25 November 2015 в 11:35
  • 4
    @RaviKuldeep нет, эта проблема возникает из-за цикла, попробуйте второй подход с 3 элементами и u должен получить ту же ошибку – nafas 25 November 2015 в 11:37

Вы не можете удалить из списка, если вы просматриваете его с циклом «для каждого».

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

Если вы используете код ниже, вы не получите никакого исключения:

private static void removeLalala(Collection<String> c) 
  {
    /*for (Iterator<String> i = c.iterator(); i.hasNext();) {
      String s = i.next();
      if(s.equals("lalala")) {
        c.remove(s);
      }
    }*/

    Iterator<String> it = c.iterator();
    while (it.hasNext()) {
        String st = it.next();
        if (st.equals("lalala")) {
            it.remove();
        }
    }
  }
1
ответ дан Shiladittya Chakraborty 16 August 2018 в 12:10
поделиться
Другие вопросы по тегам:

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