美文网首页
遍历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