欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-10 23:42:40
...

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

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

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,遍历时会进行一个赋值动作。记录遍历初始的操作变更次数(后期异常判断)

关于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
    }

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

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

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

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

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

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

 

 

 

 

 

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