• Java中循环删除list报错解析


    面试问到这个,印象中可以用迭代器解决,但是具体原理记不清楚了,整理一下

    循环遍历list的三种方式:for循环、foreach循环、iterator遍历。

    for循环

    代码示例:

    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    for(int i=0;i<list.size();i++){
        if(Integer.parseInt(list.get(i)) >=1){
            list.remove(i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这种方式有问题,问题在于删除某个元素后,list的大小发生变化,同时索引也会发生变化,所以会导致在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除元素后,后面的元素都往前移动了一位,所以实际访问的是第3个元素(debug试一下就知道了)。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

    解决方法

    1. 每遍历完一个索引,回退一步,这样可以避免跳过进位的数据

      List list = new ArrayList<>();
      list.add("1");
      list.add("2");
      list.add("3");
      for(int i=0;i=1){
              list.remove(i);
              i--;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    2. 从后面往前遍历

      List list = new ArrayList<>();
      list.add("1");
      list.add("2");
      list.add("3");
      for(int i=list.size();i>=0;i--){
          if(Integer.parseInt(list.get(i)) >=1){
              list.remove(i);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    foreach循环

    代码示例:

    List list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    for(String str:list){
    	if(Integer.parseInt(str) >=1){
    		list.remove(str);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    抛出异常:java.util.ConcurrentModificationException

    foreach 写法实际上是对的 iterator、hasNext、next方法的简写。因此从List.iterator()源码着手分析,跟踪iterator()方法,该方法返回了 Itr 迭代器对象。

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

    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;
    
            Itr() {}
    
            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();
                }
            }
    
            @Override
            @SuppressWarnings("unchecked")
            public void forEachRemaining(Consumer<? super E> consumer) {
                Objects.requireNonNull(consumer);
                final int size = ArrayList.this.size;
                int i = cursor;
                if (i >= size) {
                    return;
                }
                final Object[] elementData = ArrayList.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                while (i != size && modCount == expectedModCount) {
                    consumer.accept((E) elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    通过代码我们发现 Itr 是 ArrayList 中定义的一个私有内部类,在 next、remove方法中都会调用checkForComodification 方法,该方法的作用是判断 modCount 和 expectedModCount是否相等,如果不相等则抛出ConcurrentModificationException异常。每次正常执行 remove 方法后,都会执行expectedModCount = modCount,保证两个值相等,那么问题基本上已经清晰了,在 foreach 循环中执行 list.remove(str),对 list 对象的 modCount 值进行了修改,而 list 对象的迭代器的 expectedModCount 值未进行修改,因此抛出了ConcurrentModificationException异常。

    iterator遍历

    代码示例:

    Iterator<String> it = list.iterator();
    while(it.hasNext()){
        String x = it.next();
        if(Integer.parseInt(x) >=1){
            it.remove();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,不能使用list的remove方法。

    总结

    • 用for循环遍历List删除元素时,需要注意索引会左移的问题。
    • List删除元素时,最好使用迭代器iterator的remove方式。
  • 相关阅读:
    Python大数据之PySpark(五)RDD详解
    Vue进阶(幺陆玖)信创适配改造
    Python第三方库 - Flask(python web框架)
    pytorch深度学习实战lesson7
    芒果改进YOLOv5系列:首发结合最新NIPS2022华为诺亚的GhostNetV2 架构:长距离注意力机制增强廉价操作,打造高效轻量级检测器
    Android—百度地图的简单使用
    什么是内网穿透?
    大专专科毕业设计前端网站源码]基于html的美食网站(js)(静态网页)
    信贷反欺诈场景中策略与模型的搭建|实操一大反欺诈模型
    druid 加密数据源:如何拦截 Druid 数据源自动注入完成帐密的解密?
  • 原文地址:https://blog.csdn.net/More_speed/article/details/126232384