This is one of the most popular interview questions recently asked in a job interview.
1. Rookie mistakes
Perhaps many beginners (including me, haha) first thought of writing is the following:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add(Blog Park);
platformList.add("CSDN");
platformList.add("Nuggets");
for (String platform : platformList) {
if (platform.equals(Blog Park)) {
platformList.remove(platform);
}
}
System.out.println(platformList);
}
Copy the code
Then with confidence to run, result unexpectedly throw Java. Util. Abnormal ConcurrentModificationException, translated into Chinese is: concurrent modification abnormalities.
Are you confused, wondering why?
Let’s first look at the bytecode generated by the above code, as follows:
As you can see, the foreach loop actually executes using Iterator, using hasNext () and next() as its core methods.
Now, how is the ArrayList class Iterator implemented?
As can be seen, when the next() method is called to get the next element, the first line of code is called checkForComodification(). The core logic of the method is to compare the values of the two variables, modCount and expectedModCount.
In the example above, the modCount and expectedModCount are both 3 at the beginning, so it is ok to get the “bloggarden” element the first time, but when you finish executing the following line:
platformList.remove(platform);
Copy the code
ModCount is changed to 4.
So on the second element, modCount and expectedModCount value is not equal, so throw out the Java. Util. ConcurrentModificationException anomalies.
Since we can’t use foreach to do this, how do we do it?
There are three main methods:
- Use Iterator’s remove() method
- Use the for loop to traverse in positive order
- Use the for loop to iterate backwards
Let’s go through them.
2. Use the remove() method of Iterator
Iterator’s remove() method is implemented as follows:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add(Blog Park);
platformList.add("CSDN");
platformList.add("Nuggets");
Iterator<String> iterator = platformList.iterator();
while (iterator.hasNext()) {
String platform = iterator.next();
if (platform.equals(Blog Park)) {
iterator.remove();
}
}
System.out.println(platformList);
}
Copy the code
The output is:
[CSDN, Denver]
Iterator.remove (); Is that ok?
Let’s take a look at the source code:
You can see, every time delete an element, modCount value will be assigned to again expectedModCount, so two variables are equal, not trigger Java. Util. ConcurrentModificationException anomalies.
3. Use the for loop to traverse in positive order
Positive traversal with the for loop is implemented as follows:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add(Blog Park);
platformList.add("CSDN");
platformList.add("Nuggets");
for (int i = 0; i < platformList.size(); i++) {
String item = platformList.get(i);
if (item.equals(Blog Park)) {
platformList.remove(i);
i = i - 1;
}
}
System.out.println(platformList);
}
Copy the code
Delete an element from an array using the subscript of the array. The value of the subscript must be corrected after the element is deleted:
i = i - 1;
Copy the code
Why would I fix the value of the subscript?
Because at the beginning the index of the element looks like this:
After the first loop removes the element “blogosphere”, the subscript of the element becomes the following:
In the second loop, the value of “I” is 1, which means that the element “CSDN” is skipped, so after deleting the element, we need to correct the subscript, which is also the code above I = i-1; The purpose of.
4. Use the for loop to iterate backwards
Reverse traversal with the for loop is implemented as follows:
public static void main(String[] args) {
List<String> platformList = new ArrayList<>();
platformList.add(Blog Park);
platformList.add("CSDN");
platformList.add("Nuggets");
for (int i = platformList.size() - 1; i >= 0; i--) {
String item = platformList.get(i);
if (item.equals("Nuggets")) {
platformList.remove(i);
}
}
System.out.println(platformList);
}
Copy the code
This is done in the same way as if we were traversing through the for loop, except that we don’t have to fix the subscript, because we started with the element’s subscript:
After the first loop removes the element “nuggets”, the subscript of the element becomes the following:
In the second loop, I is 1, which means that the element CSDN is reached, which does not cause the element to be skipped, so there is no need to modify the subscript.
5. 5. Smart refrigerator
5.1 Using the removeIf() method (recommended)
As of JDK1.8, we can use removeIf() instead of remove() as an Iterator.
So the original code:
Iterator<String> iterator = platformList.iterator();
while (iterator.hasNext()) {
String platform = iterator.next();
if (platform.equals(Blog Park)) { iterator.remove(); }}Copy the code
This can be simplified to 1 line of code like this, very succinctly:
platformList.removeIf(platform -> Blog Park.equals(platform));
Copy the code
Iterator removeIf() : Iterator removeIf() : Iterator removeIf() : Iterator removeIf() : Iterator removeIf() : Iterator removeIf() : Iterator removeIf() : Iterator removeIf()
5.2 If the For loop is traversal in positive order, do I need to correct the subscript?
The conclusion: yes.
However, the previous example is not very good, so many readers think that it is ok not to modify the subscript, in fact, it is not, let’s change the example to understand:
List<String> platformList = new ArrayList<>();
platformList.add(Blog Park);
platformList.add(Blog Park);
platformList.add("CSDN");
platformList.add("Nuggets");
for (int i = 0; i < platformList.size(); i++) {
String item = platformList.get(i);
if (Blog Park.equals(item)) {
platformList.remove(i);
}
}
System.out.println(platformList);
Copy the code
Output result:
[Blog Park, CSDN, Nuggets]
If you do not fix the subscript, the second element “blog garden” will be skipped in the loop and cannot be deleted, so make sure you fix the subscript:
6. Reference
How to delete Java collections while iterating through them
Why can’t Java delete elements while iterating