Iterator and concurrent modification exceptions
People who know a little about Java Collection classes may know the problems of Iterator and concurrent modification exceptions, that is, when Iterator is used to iterate to access Collection elements, the elements in the Collection cannot be changed (for example, delete by calling the remove method of the Collection),
You can only delete the collection elements returned by the next() method last time through the remove() method of Iterator, otherwise Java. Net will be thrown util. Concurrentmodificationexception exception.
This is because the Iterator iterator adopts the fail fast mechanism. Once it is detected that the set has been modified during the iteration (usually modified by other threads in the program), the program immediately raises the 'ConcurrentModificationException' exception instead of displaying the modified result, which can avoid potential problems caused by sharing resources.
/** The implementation principle is as follows: The iterator directly accesses the contents of the collection during traversal, and uses a modCount variable during traversal. If the content of the collection changes during traversal, the value of modCount will be changed. Whenever the iterator uses hashNext()/next() to traverse the next element, it will detect whether the modCount variable is the expectedmodCount value. If so, it will return the traversal; Otherwise, a ConcurrentModificationException is thrown to terminate the traversal Note: the throw condition of the exception here is that modCount is detected= expectedmodCount this condition. */
Example code:
public class IteratorDemo { public static void main(String[] args) throws Exception{ final ArrayList<String> list = new ArrayList<>(); // final Vector<String> list = new Vector<>(); // final LinkedList<String> list = new LinkedList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); System.out.println(list); final Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { final String next = iterator.next(); System.out.println(next); if ("aaa".equals(next)) list.remove(next); } System.out.println(list); } }
The result is clearly wrong:
The following is the focus of this article. Please see the following code:
public class IteratorDemo { public static void main(String[] args) throws Exception{ final ArrayList<String> list = new ArrayList<>(); // final Vector<String> list = new Vector<>(); // final LinkedList<String> list = new LinkedList<>(); list.add("aaa"); list.add("bbb"); //list.add("ccc"); // The only difference!!! System.out.println(list); final Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { final String next = iterator.next(); System.out.println(next); if ("aaa".equals(next)) list.remove(next); } System.out.println(list); } }
What are the results? Since it is proposed here, it will certainly not throw an exception like our first intuition. On the contrary, the program 'exception' runs smoothly
(
)
Maybe no one will do as much as I do, but I'm curious. Why? And if you are interested, try adding a different number of elements. As a result, ArrayList and Vector do not throw exceptions when there are only two elements!!!
It has to be said that this is somewhat puzzling. However, in this case, we can only explore the reason with the help of the source code (on the night when we found this problem, the state was really poor. We found it after consulting our colleagues. Thank you silently here for Thanks ♪ (♪ ω・) ノ3 )
Here, you have to look at the implementation principle of Iterator in the collection class. First, look at some implementations of ArrayList:
public Iterator<E> iterator() { return new Itr(); } /** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} 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]; } // ...... final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
The focus is on the implementation of the hasNext() method. In the above example code, when executing the second time, the cursor value is 1, and the size value happens to be 1 (emmm...) because an element is deleted
hasNext() will return false
The result is obvious. You can't run the next() method of checking modCount at all. Here, the loop will be ended immediately, and the result is what the console output looks like!!!
I don't know how you feel when you see here. I'm numb anyway. Let's wash and sleep today~
PS: if you are interested, you can try Linkedlist. Its iterative implementation is slightly different from ArrayList and Vector, but the biggest difference in this case is:
public boolean hasNext() { return nextIndex < size; }
Think about whether something similar to ArrayList and Vector will happen? For example, when there is only one element, emmm