阿里java规范“不要在 foreach 循环里进行元素的 remove/add 操作,remove 元素请使用 Iterator 方式”

先来看一段代码,摘自阿里巴巴java开发规范:

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String item: list) {
	if ("1".equals(item)) {
		list.remove(item);
	}
}

可以发现是可以执行的,但事实上循环只执行了一次便停止了(原因在下文讲述),但如果要删除"2",便会报如下错误:
在这里插入图片描述
这个错误是指存在并发修改,通常在多线程下会出现,但单线程也会出现,本文的错误是因为***迭代器和list集合在同时操作集合内的内容,发生冲突***。
foreach是增强版的for,代码简洁,其底层实现是通过迭代器实现的。为了分析代码,将class文件反编译如下:

List list = new ArrayList();
list.add("1");
list.add("2");
Iterator i$ = a.iterator();
do
{
	if(!i$.hasNext())
		break;
	String temp = (String)i$.next();
	if("1".equals(temp))
	a.remove(temp);
} while (true);

hasNext()、next()、remove()方法是引起问题的关键,并且需要注意的是这里的remove()方法还是集合list的方法。
先看一下list.remove()方法的核心方法fastRemove()方法:

private void fastRemove(int index) {
	modCount++;
	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
}

可以看到在删除集合元素的同时,modCount自增了,先记住这个参数就好,下文会提。
作为对比,来看下iterator()方法:

public Iterator<E> iterator() {
	return new 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;
     
	public boolean hasNext() {
		return cursor != size;
	}
	
	@SuppressWarnings("unchecked")
    	public E next() {
        	checkForComodification();//万恶之源
            int i = cursor;
            if (i >= size)
            	throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
 
        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();
        }
}

可以看到迭代器中的remove方法让expectedModCount与modCount保证相等,这便是与list.remove()方法的区别。
下面需要几点:
1.在iterator初始化的时候,expectedModCount == modCount,并且等于集合容量大小。
2.报ConcurrentModificationException异常是因为expectedModCount != modCount。
可以分析文初的案例:
case1:执行完一次循环后,modCount = 3,expectedModCount = 2,lastRet = 0,cursor == size == 1,所以只执行了一次循环,并未报错。
case2:执行完两次循环后,modCount = 3,expectedModCount = 2,lastRet = 1,cursor = 2,size = 1,继续进入第三次循环,进入next()方法的checkForComodification()方法中,由于modCount和expectedModCount不想等,报异常!
分析到这,便有了答案,集合的add方法也是如此,这里便不赘述!

你可能感兴趣的:(辨析)