美文网首页
遍历list中对该list add/removal会发生什么

遍历list中对该list add/removal会发生什么

作者: 兰桂坊2020 | 来源:发表于2021-06-30 12:51 被阅读0次
    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    
    1. 使用fori
    public static void main(String[] args) {
      for (int i = 0; i < list.size(); i++) {
          if (list.get(i).equals("1")) {
              list.remove(i);
          }
          System.out.println("i: " + i + ", " + list.get(i));
      }
      System.out.println("list: " + list);
    }
    结果:
    i: 0, 2
    i: 1, 3
    list: [2, 3]
    分析:并不会抛异常:ConcurrentModificationException,但是2、3的索引产生改变。
    
    2. 使用增强for
    public static void main(String[] args) {
      for (String str : list) {
         if (str.equals("1")) {
           list.remove(str);
         }
      }
      System.out.println("list: " + list);
    }
    结果:
    Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
    at java.util.ArrayList$Itr.next(ArrayList.java:861)
    
    public static void main(String[] args) {
      for (String str : list) {
         if (str.equals("2")) {
           list.remove(str);
         }
      }
      System.out.println("list: " + list);
    }
    结果:
    list: [1, 3]
    
    分析:
    1. 为什么会抛异常:ConcurrentModificationException,源码里面是这样判断的:
    final void checkForComodification() {
       if (modCount != expectedModCount)
           throw new ConcurrentModificationException();
    }
    这里的两个值:modCount、expectedModCount,都是指数组修改次数,初始值为数组大小,本例中为3。
    List的remove()方法只会增加modCount的值,而不会增加expectedModCount,具体实现如下:
    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,
    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,调用remove方法后不会报错,但第二次循环的时候,调用next方法找第2个元素时,会调用checkForComodification,此时的modCount=4,而expectedModCount=3,故抛出此异常。
    2. 为什么删除1抛异常,删除2不抛异常呢?
    数组大小本来为3,删除元素2时,数组大小变为2了。元素2为倒数第二个,hasNext()时发现已没有下一个了(倒数第二个不会抛异常,其他位置均会抛异常),而数组大小也改变了,会重新初始化expectedModCount=modCount=2,故不会抛异常
    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;
    
       Itr() {}
    
        public boolean hasNext() {
            return cursor != size;
        }
        .......
    
    3. 使用iterator
    Iterator<String> it = list.iterator();
    while(it.hasNext()){
      String x = it.next();
      if(x.equals("1")){
        it.remove();
      }
    }
    结果:
    list: [2, 3]
    
    总结:使用iterator或list.removeIf(x -> x.equals("1"));
    

    相关文章

      网友评论

          本文标题:遍历list中对该list add/removal会发生什么

          本文链接:https://www.haomeiwen.com/subject/lyalultx.html