fail-fast mechanism (deleting objects in List while traversing)

There are many ways to traverse and delete elements in a List, which can cause problems when used incorrectly. Let's take a look at the following forms of traversing and deleting elements in a List:

1. Delete multiple elements that meet the conditions through an enhanced for loop

2. Remove an element that meets the conditions through an enhanced for loop

3. Delete multiple elements that meet the conditions through ordinary for deletion

4. Traverse through Iterator to delete multiple elements that meet the conditions

 

  1. /**  
  2.  * Use an enhanced for loop  
  3.  * After the non-basic data type is deleted from the List during the loop, ConcurrentModificationException will be reported when continuing to loop the List  
  4.  */    
  5. publicvoid listRemove() {     
  6.     List<Student> students = this.getStudents();    
  7.     for (Student stu : students) {    
  8.         if (stu.getId() == 2)     
  9.             students.remove(stu);    
  10.     }    
  11. }    
  1. /**  
  2.  * Use the enhanced for loop to traverse and delete the List like this, but there will be no exception if it jumps out immediately after deletion  
  3.  */    
  4. publicvoid listRemoveBreak() {     
  5.     List<Student> students = this.getStudents();    
  6.     for (Student stu : students) {    
  7.         if (stu.getId() == 2) {    
  8.             students.remove(stu);    
  9.             break;    
  10.         }    
  11.     }    
  12. }    
  1. /**  
  2.  * This kind of deletion and traversal can also be done normally without using the enhanced for loop,  
  3.  * The so-called normal here means that it will not report an exception, but the result obtained after deletion  
  4.  * The data is not necessarily correct, this is mainly because after the element is deleted, after the element is deleted  
  5.  The element index of * has changed. Assuming that there are 10 elements in the traversed list, when  
  6.  * After deleting the 3rd element, the 4th element becomes the 3rd element, and the 5th element becomes  
  7.  * The fourth one, but the index to which the program loops next is the fourth one,  
  8.  * At this time, the original 5th element is obtained.  
  9.  */    
  10. publicvoid listRemove2() {     
  11.     List<Student> students = this.getStudents();    
  12.     for (int i=0; i<students.size(); i++) {    
  13.         if (students.get(i).getId()%3 == 0) {    
  14.             Student student = students.get(i);    
  15.             students.remove(student);    
  16.         }    
  17.     }    
  18. }    

  1. /**  
  2.  * Use Iterator to delete and traverse smoothly  
  3.  */    
  4. publicvoid iteratorRemove() {     
  5.     List<Student> students = this.getStudents();    
  6.     System.out.println(students);    
  7.     Iterator<Student> stuIter = students.iterator();    
  8.     while (stuIter.hasNext()) {    
  9.         Student student = stuIter.next();    
  10.         if (student.getId() % 2 == 0)    
  11.             stuIter.remove(); //Here, use the remove method of Iterator to remove the current object. If you use the remove method of List, ConcurrentModificationException will also occur    
  12.     }    
  13.     System.out.println(students);    
  14. }    

So after seeing the above pieces of code, let's analyze his reasons. Before analyzing the reasons, let's first recognize the word fail-fast

Wikipedia says this:

The fail-fast mechanism is an error mechanism in the Java Collection. A fail-fast event may occur when multiple threads operate on the contents of the same collection.
  For example: when a thread A traverses a collection through an iterator, if the content of the collection is changed by other threads; then when thread A accesses the collection, a ConcurrentModificationException will be thrown and a fail-fast event will be generated.
To understand the fail-fast mechanism, we must first understand the ConcurrentModificationException exception. Thrown when a method detects concurrent modification of an object, but such modification is not allowed. At the same time, it should be noted that this exception will not always indicate that the object has been modified concurrently by different threads. If a single thread violates the rules, a modification exception may also be thrown.
Granted, the fail-fast behavior of an iterator is not guaranteed, it's not guaranteed that the error will occur, but fail-fast operations do their best to throw ConcurrentModificationException exceptions, so, to improve the correctness of such operations, write a dependency that depends on The procedure for this exception is the wrong thing to do, the right thing to do is: ConcurrentModificationException should only be used to detect bugs.

   When using fail-fast iterator to iterate over Collection or Map and try to directly modify the content of Collection / Map, even if it is running under a single thread, java.util.ConcurrentModificationException will be thrown.

Iterator works in a separate thread and holds a mutex lock. After the Iterator is created, a singly-linked index table pointing to the original object will be established. When the number of original objects changes, the content of the index table will not change synchronously, so when the index pointer moves backward, it will not be able to iterate. object, so according to the fail-fast principle, Iterator will throw java.util.ConcurrentModificationException immediately.

So the Iterator does not allow the object being iterated to be changed while it is working. But you can use iterator's own method remove() to remove objects. The Iterator.remove() method will maintain index consistency while removing the current iteration object.

Interestingly, ConcurrentModificationException will not be thrown if your Collection / Map object actually has only one element. That's why it is stated in the javadoc: itwould be wrong to write a program that depended on this exception forits correctness: ConcurrentModificationException should be used only to detect bugs. P.S. snippet

from ibm developerworks for the java.util.concurrent package:
     The collection classes in the java.util package all return fail-fast iterators, which means that they assume that the collection does not change its contents while the thread is iterating through the contents of the collection. If the fail-fast iterator detects that a modification operation was made during the iteration, it throws ConcurrentModificationException, which is an uncontrolled exception.
      The requirement not to change the collection during iteration often inconveniences many concurrent applications. Rather, it is better to allow concurrent modification and to ensure that the iterator provides a consistent view of the collection as long as it operates reasonably, as the iterators in the java.util.concurrent collection class do.
    Iterators returned by java.util.concurrent collections are called weakly consistent iterators. For these classes, if the element has been removed since the iteration started and has not been returned by the next() method, it will not be returned to the caller. It may or may not return to the caller if the element has been added since the iteration started. In one iteration, no matter how the underlying collection is changed, elements are not returned twice.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326011571&siteId=291194637