为什么在foreach循环中JAVA集合不能添加或删除元素

目录1. 编码强制规约2. 原因分析3. 相关知识介绍3.1. 什么是快速失败(fail-fast)?3.2. 什么是安全失败(fail-safe)呢?

1. 编码强制规约

在《阿里巴巴Java开发手册》中,针对集合操作,有一项规定,如下:

【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

public class SimpleTest {    public static void main(String[] args) {        List<String> list = Lists.newArrayList();        list.add("1");        list.add("2");        list.add("3");        list.add("4");         //正例        Iterator<String> iterator = list.iterator();        while (iterator.hasNext()) {            String item = iterator.next();            if ("1".equalsIgnoreCase(item)) {                iterator.remove();            }        }         //反例        for (String item : list) {            if ("2".equals(item)) {                list.remove(item);            }        }    }}

2. 原因分析

在循环或迭代时,会首先创建一个迭代实例,这个迭代实例的expectedModCount 赋值为集合的modCount.

每当迭代器使⽤ hashNext() / next() 遍历下⼀个元素之前,都会检测 modCount 变量与expectedModCount 值是否相等,相等的话就返回遍历;否则就抛出异常【ConcurrentModificationException】,终⽌遍历

如果在循环中添加或删除元素,是直接调用集合的add,remove方法【导致了modCount增加或减少】,但这些方法不会修改迭代实例中的expectedModCount,导致在迭代实例中expectedModCount 与modCount的值不相等,抛出ConcurrentModificationException异常

但迭代器中的remove,add方法,会在调用集合的remove,add方法后,将expectedModCount 重新赋值为modCount,所以在迭代器中增加、删除元素是可以正常运行的。

可以参考ArrayList中的内部私有类Itr、ListItr的源码

public Iterator<E> iterator() {        return new Itr();    } /**     * An optimized version of AbstractList.Itr     */    private class Itr implements Iterator<E> {        int cursor;       // index of next element to return        int lastRet = -1; // index of last element returned; -1 if no such        int expectedModCount = modCount;         Itr() {}         //删除了一些代码         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();            }        }         final void checkForComodification() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();        }   }   public E remove(int index) {        rangeCheck(index);         modCount++;        E oldValue = elementData(index);         int numMoved = size - index - 1;        if (numMoved > 0)            System.arraycopy(elementData, index+1, elementData, index,                             numMoved);        elementData[--size] = null; // clear to let GC do its work         return oldValue;    }

3. 相关知识介绍

3.1. 什么是快速失败(fail-fast)?

快速失败(fail-fast)是Java集合的⼀种错误检测机制。在使⽤迭代器对集合进⾏遍历的时候,在多线程下操作⾮安全失败(fail-safe)的集合类可能就会触发fail-fast机制,导致抛出ConcurrentModificationException异常。

另外,在单线程下,如果在遍历过程中对集合对象的内容进⾏了修改的话也会触发fail-fast机制。

举个例⼦:多线程下,如果线程1正在对集合进⾏遍历,此时线程2对集合进⾏修改(增加、删除、修改),或者线程1在遍历过程中对集合进⾏修改,都会导致线程1抛出ConcurrentModificationException异常。

3.2. 什么是安全失败(fail-safe)呢?

采⽤安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,⽽是先复制原有集合内容,在拷⻉的集合上进⾏遍历。所以,在遍历过程中对原集合所作的修改并不能被迭代器检测到,故不会抛ConcurrentModificationException异常。

到此这篇关于为什么在foreach循环中JAVA集合不能添加或删除元素的文章就介绍到这了,更多相关JAVA集合添加或删除元素内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

总结失败的原因能够让人越来越谨慎。

为什么在foreach循环中JAVA集合不能添加或删除元素

相关文章:

你感兴趣的文章:

标签云: