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.ConcurrentModificationException
an 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, Iterator
the 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 modCount
and compare expectedModCount
the values of these two variables.
In the above example, the values of sum modCount
and expectedModCount
are 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);
modCount
The value is modified to 4.
So when the element is obtained for the second time, modCount
the sum expectedModCount
values are not equal, so java.util.ConcurrentModificationException
an exception is thrown.
Since it cannot be implemented using foreach, how should we implement it?
There are mainly 3 methods:
- Use the remove() method of Iterator
- Use for loop to traverse in order
- 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, modCount
the value of will be reassigned to expectedModCount
, so that the two variables are equal and no java.util.ConcurrentModificationException
exception 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 Iterator
methods 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 Iterator
used remove()
:
Original link: [Java Interview Question] How to traverse a List and delete it at the same time? - Shanghai Stranger - Blog Park (cnblogs.com)