Remember, don't delete elements in the List collection in the for loop! ! !

Remember, don't delete elements in the List collection in the for loop! ! !

foreword

  Let's think about such a question, how should we delete the elements in the List collection?
  If we want to delete the elements in the collection, we must get every element in the collection, then the first thing we think of is to use the for loop. If you can think of this like me, then I think Thai pants are hot.
  but! ! ! No matter under what circumstances, we should not add, delete, or modify elements in the collection when using the for loop to traverse the List collection, as this will cause bugs!
  Moreover, this situation is also explicitly prohibited in the Ali development manual, as shown in the figure.
insert image description here

  So why do I say this, let's look at such an example and see what happens when using a for loop to delete elements in a collection.

example

need

  In a List collection whose element type is String, there are N elements, delete the elements containing the character ''a'' among these elements.
WRONG ANSWER 1: Use a normal for loop (for-i)

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<String> list = new ArrayList<String>();

        //调用集合的add方法向集合中添加元素
//		list.add("a");
//		list.add("ab");
//		list.add("abc");
//		list.add("acd");
//		list.add("cba");

        //也可以调用Collection类中的addAll静态方法向list集合中一次性添加元素
        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

//		 方式一:for循环进行数据删除
        for (int i = 0; i < list.size(); i++) {
    
    
            if (list.get(i).contains("a")) {
    
    
                list.remove(i);
            }
        }

        System.out.println("删除后:" + list);
    }
}

After running, we can see the following results:

删除前:[a, ab, bac, acd, abc]
删除后:[ab, acd]

It can be found that the two elements containing the 'a' character have not been deleted.

analyze

  Using a normal for loop to traverse the List collection while deleting elements will not report an error during operation. But in most scenarios, we cannot use this method, and the above results also confirm this point. Although your code will not report an error and run normally, but it cannot meet our requirements in this example, then writing it like this is a bug.
  When the element with index i is deleted, the following elements will be automatically filled forward, that is, the original element with index i+1 is now the element with index i, but the index taken by the next cycle is i+1, this When you think that you get the element with the original index i+1, you actually get the element with the original index i+2. By analogy, we will miss many target elements.
The process analysis is shown in the figure below:
insert image description here
  Then some people may say that since the ordinary for loop cannot be used, the enhanced for loop that is more advanced than the ordinary for loop must be ok.
  Here I will share with you an interview question first: What is the difference between enhanced for loop and ordinary for loop?
  Enhanced for loop is a simplified version of for loop, which is generally used to traverse arrays and collections. The internal principle is actually an Iterator iterator, so in the process of traversal Among them, the addition and deletion of elements in the collection cannot be performed. foreach is suitable for unknown loop times, or when the loop times are cumbersome, it is more efficient to use, but some more complex operations still require the use of for loops.
  So let's watch it first, let me write and run it to see the result.
Wrong answer 2: use enhanced for loop (foreach)

package javabean;

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<String> list = new ArrayList<String>();

        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

        // 方式二:增强for循环删除元素
		for (String string : list) {
    
    
			if (string.contains("a")) {
    
    
				list.remove(string);
			}
		}
        System.out.println("删除后:" + list);
    }
}

After running, we can see the following output in the console:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.yunhe.day0324.Test4.main(Test4.java:29)

  An exception broke out here: ConcurrentModificationException (concurrent modification exception). As the interview question said, the enhanced for loop is only applicable to the traversal output of the collection, and it cannot add, delete, modify and check elements.
  Then some friends will ask, since the two commonly used for loops do not work, is there no other way to delete the data in the list collection? The answer is yes, there must be. Let me introduce to you two methods for deleting elements in the List collection.

  Correct way 1: The iterator deletes the elements in the LIst collection

package javabean;

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<String> list = new ArrayList<String>();

        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

        // 通过list对象获取其迭代器对象
		Iterator<String> iterator = list.iterator();

		//while循环 判断是否还有下一个元素
		while (iterator.hasNext()) {
    
    
		//while循环内代码可用下面一行lambda写法代替:
		//list.removeIf(s -> s.contains("a"));
		
		//判断元素中是否包含'a'字符
			if (iterator.next().contains("a")) {
    
    
			//包含则删除元素
				iterator.remove();
			}
		}

        System.out.println("删除后:" + list);
    }
}

  But is our commonly used for loop really unable to complete the deletion of List collection elements?
  Of course, the answer is yes, everything is not absolute. First of all, let's summarize why we said above that the for loop cannot delete elements in the List collection. Because in the process of traversal, we delete the current element, the following elements will automatically fill in forwards, then the index will move backward at this time, then the element that was filled just now will not be judged, but will jump directly Over, so it will cause the omission of elements. At its root, the ultimate cause of bugs is that the following elements automatically fill in forward. So is there any way for us to avoid this phenomenon?
  Everyone shake your head with me and shake out the cheese. Hello everyone, I am uneducated!
  Do you have any ideas after the exercise? Will traversing elements from front to back fill in forward? Then we traverse the deleted elements from the back to the front, and we will never make up for them. No way! No way! Surely no one will think of this, if so, I can only say that you may be the next James Gosling, trust yourself!
  Correct way 2: for loop reverse traversal to delete elements

package javabean;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<String> list = new ArrayList<String>();

        Collections.addAll(list, "a", "ab", "bac", "acd", "abc");
        System.out.println("删除前:" + list);

        // 方式四:for循环倒叙遍历删除元素
		for (int i = list.size() - 1; i >= 0; i--) {
    
    
			if (list.get(i).contains("a")) {
    
    
				list.remove(i);
			}
		}
        System.out.println("删除后:" + list);
    }
}

  Thank you for your reading and support, like, follow and add favorites!
  Follow me for follow-up updates, learn Java without getting lost!

Guess you like

Origin blog.csdn.net/m0_53924139/article/details/130634678