Как итераторы Java обнаруживают, что коллекция изменена, чтобы вызвать исключение ConcurrentModificationException? - PullRequest
0 голосов
/ 07 ноября 2019

Как итераторы Java обнаруживают, что коллекция изменена? Пробовал искать, нашел:

Обычно традиционные классы коллекции в пакете java.util используют переменную int (modCount) для отслеживания изменений (добавления и удаления).

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

При вызове метода next () объект Итератор проверяет текущийзначение переменной счетчика модификаций относительно ожидаемого значения счетчика модификаций.

В случае несоответствия он быстро завершается ошибкой, выбрасывая ConcurrentModificationException, присутствующего в пакете java.util, его RuntimeException.

Но чтоесли мы

  1. Отложить итератор до изменения
  2. Изменить коллекцию
  3. создать 2-й итератор после того, как модификация произошла, и выполнить итерацию

?

Кажется, все нормально со вторым итератором, не так ли? Так как насчет modCount тогда? Модифицированная коллекция должна информировать 1-го итератора, чтобы он выдавал исключение, и в то же время не должна сообщать 2-му. Объясните пожалуйста как работает modCount? Должно быть сложно программировать поведение modCount или хранить коллекцию modCounts для каждого итератора. В любом случае, поясните, пожалуйста, как несколько итераторов проверяют свою согласованность одновременно и независимо?

1 Ответ

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

Как правило, это работает следующим образом:

class MyCollection implements Collection<E /* or whatever the elements are */> {
    private int modCount = 0;
    private class MyIterator implements Iterator<E> {
         private int expectedModCount;
         public MyIterator() {
              expectedModCount = modCount;
         }
         @Override
         public E next() {
             if(expectedModCount != modCount) throw new ConcurrentModificationException();
         }
         // etc.
    }
    @Override
    public Iterator<E> iterator() {
        return new MyIterator();
    }
    @Override
    public boolean add(E e) {
       modCount++;
       // etc.
    }
    // etc.
}

Каждый MyIterator знает , чего ожидать modCount, запоминая значение в виде поля. Ваши итераторы 1 и 2 не будут сбиты с толку, потому что они будут отдельными объектами с отдельными полями с отдельными значениями, что означает, что они будут ожидать разные modCount s. Кроме того, обратите внимание, что ConcurrentModificationException s выбрасываются "опросом", а не "уведомлением". Коллекция не должна отслеживать свои итераторы и уведомлять их о модификации при вызове метода в коллекции. Скорее, каждый итератор проверяет, была ли изменена коллекция, когда вы вызываете метод на итераторе. Если вы никогда не будете использовать итератор после изменения коллекции, у него никогда не будет возможности вызвать исключение, поэтому в вашем примере исключение не будет выдано, и это правильное поведение.

...