译者:奋斗的小程序员

    www.toutiao.com/i6754322606561690116

    编程过程中常常需要使用到集合,而ArrayList是我们常常使用的,但是最近在一次删除和增加中出现了一些问题,分享记录下。

    分下如下俩段代码

    1. List<String> arrayList1 = new ArrayList<String>();
    2. arrayList1.add("1");
    3. arrayList1.add("2");
    4. for (String s : arrayList1) {
    5. if ("1".equals(s)) {
    6. arrayList1.remove(s);
    7. }
    8. List<String> arrayList2 = new ArrayList<String>();
    9. arrayList2.add("2");
    10. arrayList2.add("1");
    11. for (String string : arrayList2) {
    12. if ("1".equals(string)) {
    13. arrayList2.remove(string);
    14. }
    15. }
    16. }

    程序运行结果如下:

    arrayList1的remove方法成功执行, arrayList2的remove方法运行抛出ConcurrentModificationException异常。

    我们查看源代码来分析异常原因
    因为foreach的本质就是使用迭代器Iterator,所有的Collecation集合类都会实现Iterable接口。
    找到ArrayList类的iterator()方法

    1. public Iterator<E> iterator() {
    2. return new Itr();
    3. }

    迭代器的本质是先调用hasNext()方法判断存不存在下一元素,然后再使用next()方法取下一元素

    1. public boolean hasNext() {
    2. return cursor != size;
    3. }
    4. @SuppressWarnings("unchecked")
    5. public E next() {
    6. checkForComodification();
    7. int i = cursor;
    8. if (i >= size)
    9. throw new NoSuchElementException();
    10. Object\[\] elementData = ArrayList.this.elementData;
    11. if (i >= elementData.length)
    12. throw new ConcurrentModificationException();
    13. cursor = i + 1;
    14. return (E) elementData\[lastRet = i\];
    15. }

    上面 arraylist1 为什么能 remove 成功呢?其实它只循环了一次,所以成功了。

    因为它在 remove 元素1之后,它的size-1变成1,然后Itr内部的cursor变量由0变成1,此时1=1,循环结束,所以成功了。

    arraylist2 为什么 remove 失败呢?因为它在循环第二次的时候,也 remove 成功了,但是第三次判断 next 的时候 cursor 的值为2导致不等于现在的 size 1,所以执行了next方法,最重要的来了,之前 remove 的操作导致ArrayList 的 modCount 值加1,然后Itr类中的 expectedModCount 保持不变,所以会抛出异常。

    1. final void checkForComodification() {
    2. if (modCount != expectedModCount)
    3. throw new ConcurrentModificationException();
    4. }

    同理可得,由于add操作也会导致modCount自增,所以不允许在 foreach 中删除, 增加,修改 ArrayList 中的元素。

    对此,推荐大家使用迭代器Iterator删除元素。

    1. Iterator<String> ite = arrayList2.iterator();
    2. while(ite.hasNext()) {
    3. if("1".equals(ite.next())) {
    4. ite.remove();
    5. }
    6. }

    如果存在并发操作,还需要对 Iterator 进行加锁操作。