Почему этот код не вызывает исключение ConcurrentModificationException?

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

List myList = new ArrayList(); myList.add("January"); myList.add("February"); myList.add("March"); Iterator it = myList.iterator(); while(it.hasNext()) { String item = it.next(); if("February".equals(item)) { myList.remove(item); } } for (String item : myList) { System.out.println(item); } 

Затем он продолжил объяснять, как решить проблему с различными предложениями.

Когда я попытался воспроизвести его, я не получил исключения! Почему я не получаю исключения?

В соответствии с документами API Java API Iterator.hasNext не генерирует исключение ConcurrentModificationException .

После проверки "January" и "February" вы удаляете один элемент из списка. Вызов it.hasNext() не it.hasNext() ConcurrentModificationException но возвращает false. Таким образом, ваш код выходит из строя. Однако последняя строка никогда не проверяется. Если вы добавите "April" в список, вы получите Exception, как ожидалось.

 import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class Main { public static void main(String args[]) { List myList = new ArrayList(); myList.add("January"); myList.add("February"); myList.add("March"); myList.add("April"); Iterator it = myList.iterator(); while(it.hasNext()) { String item = it.next(); System.out.println("Checking: " + item); if("February".equals(item)) { myList.remove(item); } } for (String item : myList) { System.out.println(item); } } } в import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class Main { public static void main(String args[]) { List myList = new ArrayList(); myList.add("January"); myList.add("February"); myList.add("March"); myList.add("April"); Iterator it = myList.iterator(); while(it.hasNext()) { String item = it.next(); System.out.println("Checking: " + item); if("February".equals(item)) { myList.remove(item); } } for (String item : myList) { System.out.println(item); } } } 

http://ideone.com/VKhHWN

Из источника ArrayList (JDK 1.7):

 private class Itr implements Iterator { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } 

Каждая операция модификации на ArrayList увеличивает поле modCount (количество раз, когда список был изменен после создания).

Когда создается iterator, он сохраняет текущее значение modCount в expectedModCount . Логика такова:

  • если список не изменяется вообще во время итерации, modCount == expectedModCount
  • если список изменен собственным методом remove() , то modCount увеличивается, но expectedModCount что значениеModCount также увеличивается, поэтому modCount == expectedModCount прежнему сохраняется
  • если какой-либо другой метод (или даже какой-то другой экземпляр iteratorа) изменяет список, modCount получает incremented, поэтому modCount != expectedModCount , что приводит к ConcurrentModificationException

Однако, как вы можете видеть из источника, проверка не выполняется в hasNext() , только в next() . Метод hasNext() также сравнивает только текущий индекс с размером списка. Когда вы удалили второй из последних элементов из списка ( "February" ), это привело к тому, что следующий вызов hasNext() просто вернул false и завершил итерацию до того, как CME мог быть выброшен.

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

Я думаю, что правильное объяснение – это выдержка из javadocs ConcurrentModificationExcetion:

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

Итак, если iterator не работает быстро, он может исключить исключение, но нет никакой гарантии. Попробуйте заменить February на January в вашем примере и исключение будет выбрано (по крайней мере, в моей среде)

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

  • kill -3, чтобы получить java thread dump
  • Избегание кода операции getfield
  • Как использовать сервлет-фильтр в Java для изменения URL-адреса входящего сервлета?
  • JasperReports: как вызвать отчет на странице jsp
  • JPanel в игре-головоломке не обновляется
  • Clone () vs Copy constructor - который рекомендуется в java
  • Передайте локальный файл в URL в Java
  • Шаблон MVC и SWING
  • Как обрабатывать новое окно в Selenium WebDriver с помощью Java?
  • Проверка наличия или отсутствия URL-адреса
  • Каков часовой пояс по умолчанию в java.util.Date
  • Давайте будем гением компьютера.