Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this code throw a ConcurrentModificationException?

Tags:

Why doesn't this code throw a ConcurrentModificationException? It modifies a Collection while iterating through it, without using the Iterator.remove() method, which is meant to be the only safe way of removing.

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C")); for (String string : strings)     if ("B".equals(string))         strings.remove("B"); System.out.println(strings); 

I get the same result if I replace the ArrayList with a LinkedList. However if I change the list to ("A", "B", "C", "D) or just ("A", "B") I get the exception as expected. What is going on? I am using jdk1.8.0_25 if that is relevant.

EDIT

I've found the following link

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078

The relevant section is

The naive solution is to add comodification checks to hasNext in AbstractList, but this doubles the cost of comodification checking. It turns out that it is sufficient to do the test only on the last iteration, which adds virtually nothing to the cost. In other words, the current implementation of hasNext:

    public boolean hasNext() {         return nextIndex() < size;     } 

Is replaced by this implementation:

    public boolean hasNext() {         if (cursor != size())             return true;         checkForComodification();         return false;     } 

This change will not be made because a Sun-internal regulatory body rejected it. The formal ruling indicated that the change "has demonstrated the potential to have significant compatibility impact upon existing code." (The "compatibility impact" is that the fix has the potential to replace silent misbehavior with a ConcurrentModificationException.)

like image 617
Paul Boddington Avatar asked Apr 18 '15 22:04

Paul Boddington


People also ask

Does the ConcurrentModificationException occur?

The ConcurrentModificationException generally occurs when working with Java Collections. The Collection classes in Java are very fail-fast and if they are attempted to be modified while a thread is iterating over it, a ConcurrentModificationException is thrown.

How do I fix ConcurrentModificationException in Java?

How do you fix Java's ConcurrentModificationException? There are two basic approaches: Do not make any changes to a collection while an Iterator loops through it. If you can't stop the underlying collection from being modified during iteration, create a clone of the target data structure and iterate through the clone.

Can a ArrayList throws ConcurrentModificationException?

ArrayLists will generally throw concurrent modification exceptions if you modify the list structurally while accessing it through its iterator (but even this is not an absolute guarantee). Note that in your example you are removing elements from the list directly, and you are not using an iterator.

What iterator can throw ConcurrentModificationException?

Iterator implementations that throw ConcurrentModificationException are known as fail-fast iterators, as they fail quickly and cleanly, rather than risking arbitrary, non-deterministic behavior later.

What is ConcurrentModificationException and how to fix it?

The ConcurrentModificationException occurs when we iterate the collection class by Iterator and modify the collection during the iteration. The ConcurrentModificationException belongs to the Iterator, not to the Collection classes. Now let’s see why Iterator gives exception.

When does a thread throw an exception when modifying a collection?

Note: It is not mandatory that this exception will be thrown only when some other thread tries to modify a Collection object. It can also happen if a single thread has some methods called which are trying to violate the contract of the object.

What does it mean when an iterator throws an exception?

This may happen when a thread is trying to modify the Collection object while it is being iterated by some fail-fast iterator, the iterator will throw the exception. This message says that the exception is thrown when the next method is called as the iterator is iterating the list and we are making modifications in it simultaneously.

What is an exception in programming?

An unaccepted, unwanted event that disturbed the normal flow of a program is called an Exception. Most of the time exception is caused by our program and these are recoverable.


1 Answers

As a general rule, ConcurrentModificationExceptions are thrown when the modification is detected, not caused. If you never access the iterator after the modification, it won't throw an exception. This minute detail makes ConcurrentModificationExceptions rather unreliable for detecting misuse of data structures, unfortunately, as they only are thrown after the damage has been done.

This scenario doesn't throw a ConcurrentModificationException because next() doesn't get called on the created iterator after the modification.

For-each loops are really iterators, so your code actually looks like this:

List<String> strings = new ArrayList<>(Arrays.asList("A", "B", "C")); Iterator<String> iter = strings.iterator(); while(iter.hasNext()){     String string = iter.next();     if ("B".equals(string))         strings.remove("B"); } System.out.println(strings); 

Consider your code running on the list you provided. The iterations look like:

  1. hasNext() returns true, enter loop, -> iter moves to index 0, string = "A", not removed
  2. hasNext() returns true, continue loop -> iter moves to index 1, string = "B", removed. strings now has length 2.
  3. hasNext() returns false (iter is currently at the last index, no more indices to go), exit loop.

Thus, as ConcurrentModificationExceptions are thrown when a call to next() detects a that a modification has been made, this scenario narrowly avoids such an exception.

For your other two results, we do get exceptions. For "A", "B", "C", "D", after removing "B" we are still in the loop, and next() detects the ConcurrentModificationException, whereas for "A", "B" I'd imagine it's some kind of ArrayIndexOutOfBounds that's being caught and re-thrown as a ConcurrentModificationException

like image 126
Mshnik Avatar answered Oct 01 '22 03:10

Mshnik