在java中,增强for循环集合,不能对集合中的元素进行增删操作

1.先看看ArrayList对象调用iterator()返回的Iterator对象

public Iterator<E> iterator() {
        return new Itr();
    }

2.再看看ArrayList中实现Iterator的内部类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
    ...
}   

cursor表示下一个返回元素的下标,可以理解成 游标;lastRet表示上一次返回的元素下标。另ArrayList有个size属性,表示ArrayList中的元素个数。

3.看迭代器的hasNext()方法

public boolean hasNext() {
            return cursor != size;
        }

hasNext() 的判断条件是cursor != size. 只要没遍历到最后一个元素,就返回true.

4.下面是next()

public E next() {
    ...
    int i = cursor; // cursor为当前需要返回元素的下标
    ...
    cursor = i + 1; // cursor向后移动一个位置,指向下一个要返回的元素
    return (E) elementData[lastRet = i]; // 对lastRet赋值,然后返回当前元素
}

5.现在看下例子

ArrayList list = new ArrayList();
list.add("e1");
list.add("e2");
Iterator var2 = list.iterator();
while(var2.hasNext()) {
    String str = (String)var2.next();
    if("e1".equals(str)) {
        list.remove("e1");
    }
}

6.解释:

  • 第一次 调用var2.hasNext(),此时满足条件 cursor(0) != size(2),然后执行 var2.next(),此时 cursor=1
    执行 list.remove(“e1”),此时,list的size将从2变为1
  • 当执行完第一次循环,进入第二次hasNext()判断时,cursor=1而且size=1,导致Iterator认为已经遍历结束,因此e2将被漏掉。
  • 此时,过程已非常清楚。list本有2个元素,Iterator第一次获取元素时,程序删掉了当前元素,导致list的size变为1。Iterator第二次获取元素时,开心说到:”list一共只有一个元素,我已经遍历了一个,easy,轻松搞定!”。

矛盾点在于:hasNext() 是根据已fetch元素和被遍历对象的size动态判断的,一旦遍历过程中被遍历对象的size变化,就会出现以上问题。

7.正确的姿势,在使用迭代器遍历的时候对集合元素进行增删
将remove操作交给Iterator来处理,使用Iterator接口提供的remove操作。

List<String> list = new ArrayList<>();
list.add("e1");
list.add("e2");

for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String str = iterator.next();
    if ("e1".equals(str)) {
        iterator.remove();
    }

    if ("e2".equals(str)) {
        System.out.println("element 2 fetched");
    }
}
  • 运行结果:element 2 fetched 被正常打印出来。

  • 那Iterator的remove()又是怎么做的?下面是ArrayList中迭代器的remove方法。

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet); // 调用ArrayList的remove移除元素,且size减1
        cursor = lastRet; // 将游标回退一位
        lastRet = -1; // 重置lastRet
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

因为Iterator.remove()在执行集合本身的remove后,同时对游标进行了 “校准”。

参考博客的链接:https://blog.csdn.net/myle69/article/details/81125067

全部评论

相关推荐

totoroyyw:千年老妖😂
投递华为等公司10个岗位
点赞 评论 收藏
分享
无情咸鱼王的秋招日记之薛定谔的Offer:好拒信,偷了,希望有机会用到
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务