Home  >  Q&A  >  body text

为什么java不要在foreach循环里进行元素的remove/add操作


选自《阿里巴巴JAVA开发手册》

图1代码执行情况是:解释删除1这个元素不会报错,但是删除2这个元素报错了,这个情况如何解释?

PHPzPHPz2716 days ago1135

reply all(8)I'll reply

  • 大家讲道理

    大家讲道理2017-04-18 10:56:42

    You can know the source of the error from the reported error checkForComodification() ,如果要避免错误需要保持 modCount != expectedModCount false
    list.remove(Object)会去调用fastRemove(int)方法,这个时候必然会去修改 modCount ,这个时候就会出现错误。
    Iterator<String> iterator = list.iterator() ;这个方法的实现就是返回一个内部类 Itr,(迭代的过程都是使用的这个类),但是为什么这个 iterator.remove() 不会出现错误了,原因在与这个方法的实现是在进行实际的 ArrayList.this.remove 之前进行的 checkForComodfication 检查,remove 之后又使 expectedModCount = modCount, so no error will occur.

    Itr.remove implementation

    public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }

    If there is anything wrong, please point it out @ChaCha哥 @puluyinyi

    reply
    0
  • PHP中文网

    PHP中文网2017-04-18 10:56:42

    In the case of a single thread, when deleting elements when traversing the List, you must use the remove method of Iterator instead of the remove method of List, otherwise a ConcurrentModificationException will occur. Just imagine if a teacher is counting the number of students in the entire class, and if the students don't follow the rules and go out and come in, the teacher will definitely not be able to count them.

    In the case of multi-threading, refer to one of my blogs: http://xxgblog.com/2016/04/02...

    reply
    0
  • 天蓬老师

    天蓬老师2017-04-18 10:56:42

    First of all, this involves multi-threaded operations. Iterator does not support multi-threaded operations. The List class will maintain a modCount variable internally to record the number of modifications
    Example: ArrayList source code

    protected transient int modCount = 0;

    Every time an Iterator is generated, the Iterator will record the modCount. Each time the next() method is called, the record will be compared with the modCount of the external class List. If it is found to be unequal, a multi-threaded editing exception will be thrown.

    Why do you do this? My understanding is that you created an iterator, and the iterator is tightly coupled with the content of the collection to be traversed, which means that the content of the collection corresponding to this iterator is the current content. I definitely don’t want to do it in my bubble sort. When , there are still threads inserting data into my collection, right? So Java uses this simple processing mechanism to prevent the collection from being modified during traversal.

    As for why deleting "1" is enough, the reason lies in the hasNext() method of foreach and iterator. The syntactic sugar of foreach is actually

    while(itr.hasNext()){
        itr.next()
    }

    So every loop will execute hasNext() first, so take a look at how ArrayList’s hasNext() is written:

    public boolean hasNext() {
        return cursor != size;
    }

    cursor is a variable used to mark the position of the iterator. The variable starts from 0, and each time next is called, it performs a +1 operation, so:
    After your code deletes "1", size=1, cursor=1, this When hasNext() returns false, the loop ends, so your iterator does not call next to find the second element, so there is no way to detect modCount, so there will be no multi-threaded modification exception
    But when you delete "2" , the iterator called next twice. At this time, size=1, cursor=2, and hasNext() returned true, so the iterator foolishly called next() again, which also caused modCount to be unequal and threw Exception for multi-threaded modification.

    When your collection has three elements, you will magically find that deleting "1" will throw an exception, but deleting "2" will not be a problem. The reason is that the execution order of the above program is Consistent.

    reply
    0
  • 黄舟

    黄舟2017-04-18 10:56:42

    Because the number in the collection changes when you add or delete elements, problems may occur when traversing. For example, if a collection has 10 elements, it should be traversed 10 times. When you add Or if an element is deleted, the number of traversals is incorrect, so an error will be reported

    reply
    0
  • PHPz

    PHPz2017-04-18 10:56:42

    Just delete them in reverse order. Anyway, try not to remove the list. You can add delete mark

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-18 10:56:42

    The yellow description in the document is very interesting.

    The execution result of this example will be beyond everyone's expectation. So try replacing "1" with "2", will it have the same result?

    You still need to look at the source code of ArrayList, you will know it at a glance.

    reply
    0
  • 巴扎黑

    巴扎黑2017-04-18 10:56:42

    Just delete it in reverse order

    reply
    0
  • PHP中文网

    PHP中文网2017-04-18 10:56:42

    ArrayList is not thread-safe, which means you modify the List while traversing.
    ArrayList will throw a concurrent modification exception in this case.

    reply
    0
  • Cancelreply