почему векторные методы Iterator и ListIterator быстро терпят неудачу - PullRequest
3 голосов
/ 18 декабря 2010

В соответствии с http://download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html

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

Не могли бы вы дать мнеПример для проверки вышеупомянутого набора операторов? Мне все еще непонятно с ошибкой быстрое поведение векторного метода Iterator и ListIterator .? Confused: - ((

Ответы [ 3 ]

8 голосов
/ 18 декабря 2010

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

Вот пример:

import java.util.*;

public class Test {

    public static void main(String[] args) {
        List<String> strings = new Vector<String>();

        strings.add("lorem");
        strings.add("ipsum");
        strings.add("dolor");
        strings.add("sit");

        int i = 0;

        Iterator<String> iter = strings.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());

            // Modify the list in the middle of iteration.
            if (i++ == 1)
                strings.remove(0);
        }
    }
}

Выход:

lorem
ipsum
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)
    at Test.main(Test.java:18)

Программа выполняет следующие действия:

  1. Создает вектор и получает итератор
  2. Звонит next() дважды.
  3. Изменяет вектор (удаляя первый элемент)
  4. Повторный вызов next() (после изменения вектора)
  5. Это приводит к выбрасыванию ConcurrentModificationException.

Поскольку циклы Java for-each зависят от итераторов, эти конструкции также могут выдавать исключения ConcurrentModificationExceptions. Решение состоит в том, чтобы сделать копию списка перед итерацией (таким образом, вы выполняете итерацию по копии) или использовать, например, CopyOnWriteArrayList следующим образом:

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class Test {

    public static void main(String[] args) {
        List<String> strings = new CopyOnWriteArrayList<String>();

        strings.add("lorem");
        strings.add("ipsum");
        strings.add("dolor");
        strings.add("sit");

        int i = 0;

        Iterator<String> iter = strings.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());

            // Modify the list in the middle of iteration.
            if (i++ == 1)
                strings.remove(0);
        }
    }
}

Выход:

lorem
ipsum
dolor
sit
2 голосов
/ 18 декабря 2010

Простой способ вызвать исключение одновременной модификации -

List<String> strings = new ArrayList<String>();
strings.add("a");
strings.add("b");
for(String s: strings)
  strings.remove(s);

Это вызывает исключение, потому что коллекция изменяется во время итерации по коллекции.

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

Более новые коллекции параллелизма по-разному обрабатывают параллельную модификацию и вообще не делают этого. Они были внедрены в ядро ​​Java в 2004 году. Я предлагаю вам взглянуть на эти новые коллекции.

Кстати: не используйте Vector, если нет необходимости.

1 голос
/ 18 декабря 2010

Скажем, у вас есть вектор целых чисел, содержащий 1-10, и вы хотите удалить нечетные числа. Вы перебираете этот список, ища шансы и используя метод Iterators remove(). После этого у вас есть некоторый код, который, конечно, предполагает, что в векторе нет нечетных чисел. Если другой поток изменяет вектор во время этого процесса, иногда может существовать нечетное число (в зависимости от состояния гонки), что нарушает код, который следует после. Возможно, это даже не сломалось сразу; может быть, это не вызывает проблемы до нескольких часов или дней спустя - очень трудно устранить неисправность. Вот что происходит с методом elements().

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

Итераторы, возвращаемые iterator() и listIterator(), активно следят за неожиданными изменениями в базовом списке. Класс Vector (фактически его родительский объект AbstractList) увеличивает счетчик каждый раз, когда он изменяется. При создании итераторов для вектора они сохраняют копию счетчика модификаций вектора. Каждый раз, когда вы вызываете next() или remove(), итератор сравнивает свое сохраненное значение для счетчика с фактическим счетчиком вектора. Если они различаются, выдается исключение ConcurrentModificationException.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...