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

    技术2024-10-05  57

    先来看一段代码,摘自阿里巴巴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方法也是如此,这里便不赘述!

    Processed: 0.012, SQL: 9