I believe many students encountered the following Exception when processing the List,

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
	at java.util.ArrayList$Itr.next(ArrayList.java:831)
Copy the code

Without further ado, here are some examples to illustrate the problems and analyze the reasons.

Patients with a

package main.java.mo.basic;

import java.util.ArrayList;

/**
 * Created by MoXingwang on 2017/7/2.
 */
public class ConcurrentModificationExceptionTest {
    public static void main(String[] args) {
        ArrayList<String> strings = new ArrayList<String>();
        strings.add("a");
        strings.add("b");
        strings.add("c");
        strings.add("d");
        strings.add("e");

        for (String string : strings) {
            if ("e".equals(string)) { strings.remove(string); }}}}Copy the code
  • The execution result
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
	at java.util.ArrayList$Itr.next(ArrayList.java:831)
	at main.java.mo.basic.ConcurrentModificationExceptionTest.main(ConcurrentModificationExceptionTest.java:17)
Copy the code
  • The analysis reason

First of all, we know that the implementation principle of the enhancement for loop is the Iterator interface, which is very important, with this knowledge we can analyze why exceptions occur, this knowledge is also the most important and core.

The exception is thrown from the line “for (String String: strings) {“, so how could this line go wrong? This line of code calls two methods of the Iterator implementation class, hasNext() and next().

ArrayList. Remove (Object O) ArrayList. Remove (Object O) ArrayList

int cursor;       // index of next element to returnint lastRet = -1; // index of last element returned; - 1if no such
int expectedModCount = modCount;

public boolean hasNext() {
    returncursor ! = 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 voidcheckForComodification() {
 if(expectedModCount ! = ArrayList.this.modCount) throw new ConcurrentModificationException(); }Copy the code
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true; }}else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true; }}return false;
}

/*
 * Private remove method that skips bounds checking and does not
 * return the value removed.
 */
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}
Copy the code

We can see that when we call remove(Object o), the size of the ArrayList Object is reduced by one, size==4, modCount++ is now in place, and then the cursor in the Iterator is set to ==5, hasNext returns true, As a result, the enhancement for loops to find the next element and calls the next() method. When checkForComodification is performed, it is found that modCount is inconsistent with the expectedModCount in Iterator. Illustrate the ArrayList object has been modified, in order to prevent errors, throw an exception ConcurrentModificationException.

Itr implements Iterator (); Itr implements Iterator (); Itr implements Iterator (); There are two important attributes; Size and modCount in ArrayList; As well as the Itr cursor and expectedModCount, they are theoretically synchronous, but we can cause them to be inconsistent during some operations, such as in this example, we call the arrayList.remove () method, The size and modCount attributes were modified, but the CURSOR and expectedModCount in Itr were not changed. When the enhanced for loop was executed again, the method in Itr was called, and the data inconsistency was finally found. This is the fundamental cause of this example ConcurrentModificationException.

Now that we have analyzed the problem clearly, how can we solve it? Here we work backwards along this line of thinking, listing centralized solutions.

  • To solve the problem

    • Do not use the enhanced for loop

    For this example, it is clear that the exception is caused by a discrepancy between the properties in the ArrayList and those in the inner Itr class, so we can assume that the Itr class was not designed for the for loop and remove operations. Yes, it’s clear. Simple as that. Say nothing and start with the code.

    ArrayList<String> strings = new ArrayList<String>();
    strings.add("a");
    strings.add("b");
    strings.add("c");
    strings.add("d");
    strings.add("e");
    
    for (int i = 0; i < strings.size(); i++) {
        String element = strings.get(i);
        if("e".equals(element)){ strings.remove(element); i --; // You need to manually maintain index}}Copy the code

    The awkward part about handling remove in this way is that you need to manually maintain the index to avoid missing data.

    • Use the remove methods in Iterator, not to be confused with the remove methods in ArrayList

    Using the remove method of the Itr class, it is a good idea to iterate over objects using Itr. In the code.

    ArrayList<String> strings = new ArrayList<String>();
    strings.add("a");
    strings.add("b");
    strings.add("c");
    strings.add("d");
    strings.add("e");
    
    Iterator<String> iterator = strings.iterator();
    while (iterator.hasNext()){
        String element = iterator.next();
        if("e".equals(element)){ iterator.remove(); }}Copy the code
    • I’m not going to iterate on the list anymore, I’m going to removeAll the exception is going to iterate on the list and remove the element. All right, you’re on the right track.
    ArrayList<String> strings = new ArrayList<String>();
    strings.add("a");
    strings.add("b");
    strings.add("c");
    strings.add("d");
    strings.add("e");
    
    ArrayList<String> tempStrings = new ArrayList<String>();
    for (String string : strings) {
        if("e".equals(string)){
            tempStrings.add(string);
        }
    }
    strings.removeAll(tempStrings);
    Copy the code
    • There are always other ways of thinking, such as adding a lock to make sure the data is correct, removing it to verify that you implement an ArrayList, however you want to play with it, and using the CopyOnWriteArrayList under the java.util.Concurrent package is convenient. There are many ways to be happy.

Example 2

Example two, ArrayList, let’s try LinkedList.

package main.java.mo.basic;

import java.util.LinkedList;

/**
 * Created by MoXingwang on 2017/7/2.
 */
public class ConcurrentModificationExceptionTest {
    public static void main(String[] args) {
        LinkedList<String> strings = new LinkedList<String>();
        strings.add("a");
        strings.add("b");
        strings.add("c");
        strings.add("d");
        strings.add("e");

        for (String string : strings) {
            if ("e".equals(string)) { strings.remove(string); }}}}Copy the code

This code is the same as example 1, except that ArrayList is replaced with LinkedList. Suddenly, this code does not report an error. Isn’t this some kind of business? Okay, one more piece of code.

package main.java.mo.basic;

import java.util.LinkedList;

/**
 * Created by MoXingwang on 2017/7/2.
 */
public class ConcurrentModificationExceptionTest {
    public static void main(String[] args) {
        LinkedList<String> strings = new LinkedList<String>();
        strings.add("a");
        strings.add("b");
        strings.add("c");
        strings.add("d");
        strings.add("e");
        strings.add("f");
        strings.add("g");

        for (String string : strings) {
            if ("e".equals(string)) { strings.remove(string); }}}}Copy the code

Execute this code again and return something like this:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:953)
	at java.util.LinkedList$ListItr.next(LinkedList.java:886)
	at main.java.mo.basic.ConcurrentModificationExceptionTest.main(ConcurrentModificationExceptionTest.java:19)
Copy the code

The analysis method is exactly the same as example 1. It must be very simple to find the answer according to the analysis of Example 1, so there is no need to cite examples.

conclusion

In general, this paper is to not happened ConcurrentModificationException deeply involved, but understanding method and train of thought are the same, the two examples in the article tells us that when in dealing with Iterable implementation class do elements remove operation, And when you do it in the for loop, understanding these things will help you avoid bugs and errors.