Delete elements during List collection traversal

1. Common mistakes made by novices

Maybe many novices (including me back then, haha) first thought of writing like this:

public static void main(String[] args) {
    List<String> platformList = new ArrayList<>();
    platformList.add("博客园");
    platformList.add("CSDN");
    platformList.add("掘金");

    for (String platform : platformList) {
        if (platform.equals("博客园")) {
            platformList.remove(platform);
        }
    }

    System.out.println(platformList);
}

Then I ran it with full confidence, but java.util.ConcurrentModificationExceptionan exception was thrown. The translation into Chinese is: concurrent modification exception.

Are you confused and wondering why?

Let’s first take a look at the bytecode generated by the above code, as shown below:

It can be seen from this that when the foreach loop is actually executed, Iteratorthe core method used is hasnext()and next().

Then let’s take a look at how the Iterator of the ArrayList class is implemented?

It can be seen that next()when calling the method to get the next element, the first line of code is called checkForComodification();, and the core logic of the method is to compare modCountand compare expectedModCountthe values ​​of these two variables.

In the above example, the values ​​of sum modCountand expectedModCountare both 3 at the beginning, so there is no problem in getting the element "Blog Garden" for the first time, but when the following line of code is executed:

platformList.remove(platform);

modCountThe value is modified to 4.

So when the element is obtained for the second time, modCountthe sum expectedModCountvalues ​​are not equal, so java.util.ConcurrentModificationExceptionan exception is thrown.

Since it cannot be implemented using foreach, how should we implement it?

There are mainly 3 methods:

  1. Use the remove() method of Iterator
  2. Use for loop to traverse in order
  3. Use a for loop to traverse in reverse order

Next, we will explain them one by one.

2. Use the remove() method of Iterator

The implementation using the remove() method of Iterator is as follows:

public static void main(String[] args) {
    List<String> platformList = new ArrayList<>();
    platformList.add("博客园");
    platformList.add("CSDN");
    platformList.add("掘金");

    Iterator<String> iterator = platformList.iterator();
    while (iterator.hasNext()) {
        String platform = iterator.next();
        if (platform.equals("博客园")) {
            iterator.remove();
        }
    }

    System.out.println(platformList);
}

The output is:

[CSDN, Nuggets]

Why iterator.remove();can it be used?

Let's take a look at its source code:

It can be seen that every time an element is deleted, modCountthe value of will be reassigned to expectedModCount, so that the two variables are equal and no java.util.ConcurrentModificationExceptionexception will be triggered.

3. Use for loop to traverse in order

The implementation of positive order traversal using a for loop is as follows:

public static void main(String[] args) {
    List<String> platformList = new ArrayList<>();
    platformList.add("博客园");
    platformList.add("CSDN");
    platformList.add("掘金");

    for (int i = 0; i < platformList.size(); i++) {
        String item = platformList.get(i);

        if (item.equals("博客园")) {
            platformList.remove(i);
            i = i - 1;
        }
    }

    System.out.println(platformList);
}

This implementation method is easier to understand. It is to delete through the subscript of the array. However, one thing to note is that after deleting the element, the value of the subscript must be corrected:

i = i - 1;

Why do we need to correct the subscript value?

Because the subscript of the element at the beginning is like this:

After the element "Blog Garden" is deleted in the first cycle, the subscript of the element becomes as follows:

In the second loop, the value of i is 1, which means that the element "Nuggets" is obtained. This causes the element "CSDN" to be skipped and checked. Therefore, after deleting the element, we need to correct the subscript, which is also the above i = i - 1;usage in the code .

4. Use for loop to traverse in reverse order

The implementation of reverse order traversal using a for loop is as follows:

public static void main(String[] args) {
    List<String> platformList = new ArrayList<>();
    platformList.add("博客园");
    platformList.add("CSDN");
    platformList.add("掘金");

    for (int i = platformList.size() - 1; i >= 0; i--) {
        String item = platformList.get(i);

        if (item.equals("掘金")) {
            platformList.remove(i);
        }
    }

    System.out.println(platformList);
}

This implementation is similar to using a for loop to traverse in order, but there is no need to correct the subscript, because the subscript of the element at the beginning is like this:

After the element "Nuggets" is deleted in the first cycle, the subscript of the element becomes as follows:

In the second loop, the value of i is 1, which means that the element "CSDN" is obtained, which will not cause the element to be skipped, so there is no need to correct the subscript.

5. Use the removeIf() method (recommended)

Starting from JDK1.8, you can use removeIf()methods instead of  Iteratormethods remove()to achieve deletion while traversing. In fact, IDEA will also prompt:

So the original code:

Iterator<String> iterator = platformList.iterator();
while (iterator.hasNext()) {
    String platform = iterator.next();
    if (platform.equals("博客园")) {
        iterator.remove();
    }
}

It can be simplified to 1 line of code as shown below, which is very concise:

platformList.removeIf(platform -> "博客园".equals(platform));

If you look at the source code of the removeIf() method, you will find that the underlying method is also Iteratorused remove():

Original link: [Java Interview Question] How to traverse a List and delete it at the same time? - Shanghai Stranger - Blog Park (cnblogs.com)

Guess you like

Origin blog.csdn.net/qq_62767608/article/details/132480396