关于list在遍历中,做出删除操作的坑

首先介绍的是阿里规约上的一个案例:

建议如果有时间的同学可以去阿里云考一下这个认证,考试通过会发一个电子认证证书,有效期两年。

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

以上代码的执行结果肯定会出乎大家的意料,那么试一下把if中的“1”换成“2”,会是同样的结果吗? 请自行尝试执行。


下面介绍一个ArrayList中的一个变量 modCount  该值用于记录当前list变更操作的次数。

  • 首先初始化list:
List<String> list = new ArrayList<String>();
  • 然后开始赋值:
list.add("1");
list.add("2");

这个步骤会先判断空间,满足的情况下,将modCount+1,再将值放入elementdata数组中(默认长度10)。

  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!  增长变更次数
        elementData[size++] = e;
        return true;
    }

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//此处变更次数增加

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


开始正文

 public static void main(String[] args) {
        //初始化list
        List<String> list = new ArrayList<String>();
        //开始赋值
        list.add("1");
        list.add("2");
        //开始遍历
        for (String item : list) {
            System.out.println(item);
            //先从删除1开始
            if ("1".equals(item)) {
                list.remove(item);
            }
        }
        System.out.println("success");
    }
  • 1.初始化 & 赋值
  • 2.※开始遍历list,遍历时会进行一个赋值动作。记录遍历初始的操作变更次数(后期异常判断)

  • 3.遍历前先进行判断,当前游标是否已经完成任务。(注意这个地方后续会用到)
  public boolean hasNext() {
            return cursor != size;
        }
  • 4.获取下一元素会先进行判断 checkForComodification(),判断读取时原始的操作次数和现有的操作次数。
 final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

           判断通过返回当前位置的值。不通过 就是上面那个异常了。

  • 5.然后进入remove,进行循环判断匹配,最后fastRemove()
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

fastRemove会操作modCount++;

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    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
    }

只输出了1且成功了。为什么没有遍历到2 ?为什么没有报错?

  • 6.回看第2步,因为步骤5删除元素1的时候,size已经发生变更。遍历第二次的时候判断,已经遍历完毕(当前游标=size)
hasNext(cursor)0 != (size)2      :          true
removesize->1;(元素1被移除)
hasNext(cursor)1 != (size)1      :          false

 

 

 

 

7.直接结束循环,不会发生获取到第二个元素,所以也不会进行判定异常。


 

 public static void main(String[] args) {
        //初始化list
        List<String> list = new ArrayList<String>();
        //开始赋值
        list.add("1");
        list.add("2");
        //开始遍历
        for (String item : list) {
            System.out.println(item);
            //从删除2开始
            if ("2".equals(item)) {
                list.remove(item);
            }
        }
        System.out.println("success");
    }

遍历结束,但是抛出了异常。

  • 8.因为在删除元素2的时候,size发生了变化,导致了循环继续进行
hasNext(cursor)0 != (size)2      :          true
hasNext(cursor)1 != (size)2      :          true
removesize->1;(元素1被移除)    modCount++
hasNext(cursor)2 != (size)1      :          true

 

 

 

 

 

  • 9.遍历成功 然后进入步骤4.就发生了异常。

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值