Java 中的快速迭代器失败


public class Alpha {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<String>();
        al.add("a");
        al.add("b");
        al.add("c");
        al.add("d");
        al.add("e");

        Iterator<String> itr = al.listIterator();
        while(itr.hasNext()){
            al.remove("d"); // Throws a Concurrent Modification @ this line
            if(itr.next().equals("d")){
                //itr.remove();
                al.remove("d"); // No error on this line
            }
        }
        System.out.println(al);
    }
}

换句话说,如果将"al.remove("d")放置在if构造中,则不会抛出ConcurrentModificationException,而作为同一行代码,如果放置在if -Construct之外,则会抛出异常。请解释一下!

这是因为当您使用 itr.next() 获取下一个元素时,它将检查修改并检查集合的大小是否已更改。到那时if (modCount != expectedModCount)条件就会成真。而hasNext()方法仅返回true或false,基于当前游标点。

如果您愿意,itr.next()然后在列表中调用 move,那么它也将更新 expectedModCount 变量。检查数组列表迭代器的删除方法。

我添加了一行(第 13 行)和一些注释行号(第 18 行、第 19 行和第 21 行)。

现在,ConcurrentModidificationexception 不是在第 18 行抛出的,而是在第 19 行抛出的。但是,是的,由于执行第 18 行,它被抛出。

另外,我相信您一次使用第18行和第21行的代码。它们不会同时出现在代码中。

import java.util.*;
public class HelloWorld{
public static void main(String []args){
    ArrayList<String> al = new ArrayList<String>();
    al.add("a");
    al.add("b");
    al.add("c");
    al.add("d");
    al.add("e");
    al.add("f"); // line 13

    Iterator<String> itr = al.listIterator();
    while(itr.hasNext()){
        al.remove("d"); // line 18
        if(itr.next().equals("d")){  // line 19
            //itr.remove();
            al.remove("d"); // line 21
        }
    }
    System.out.println(al);
}
}

让我们评论第 13 行。所以这基本上是你的代码,有一些额外的注释。

如果我们检查迭代器的 next() 的实现,我们就会知道它在一开始就检查任何修改(从而抛出 CoMo)。之后,它只返回元素。

因此,如果我们从列表中删除元素而不使用第 18 行的迭代器,则会在以下 next() 调用中检测到。但是,如果您在 next() 方法之后删除元素(在第 21 行;现在已注释第 18 行),则只会在随后的 next() 中检测到 CoMo。在您的情况下,我们在打印"e"后用完了元素。所以我们从未执行后续的 next(),也没有收到异常。

现在,如果我们取消注释第 13 行(它添加了一个新元素),那么我们将在第 19 行获得 ConcurrentModidificationexception,即当执行下一个 next() 时。这证明我们将获得 CoMo 异常,只是我们将在再迭代一次后获得它。

重复:遍历列表,避免在循环中删除时出现并发修改异常在 Java 中迭代集合时从集合中删除项

只是你不能在迭代中删除元素。

看看迭代器是如何实现ArrayList

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();
 }
}

因此,它检查并发修改,使用公共 ArrayList remove 方法删除元素,并增加列表修改的计数器,以便在下一次迭代时不会抛出 ConcurrentModificationException。

就像您正在修改其他人当前正在使用的文件(此处为ArrayList)(此处为迭代循环)。

while(itr.hasNext()){
            if(itr.next().equals("d"))
            {
                //itr.remove();
                al.remove("d"); // No error on this line
            }
        }
        System.out.println(al);

使用此代码行。希望这会有所帮助

正如@VimalBera之前的回答中指出的那样:您必须使用 itr.next() 获取下一个元素,然后才能在List上调用 remove 。

但是,我发现有一些替代方法更适合此用例。第一种是在删除元素时使用迭代器

public static void main(String[] args) {
    List<String> al = new ArrayList<>();
    al.add("a");
    al.add("b");
    al.add("c");
    al.add("d");
    al.add("e");
    for (Iterator<String> itr = al.listIterator(); itr.hasNext(); ) {
        String s = itr.next();
        if ("d".equals(s)) {
            itr.remove();
        }
    }
    System.out.println(al);
}

请注意,使用 for 循环而不是while循环。这会将迭代器的范围缩小到循环本身(这很好,请始终尽量缩小范围)。

另一种方法是使用 Java 8 Streams,尤其是 filter 方法。但是,这将创建一个全新的列表,并且不会真正修改基础列表,但它提供了一个非常好的编程模型。

public static void main(String[] args) {
    List<String> al = Arrays.asList("a", "b", "c", "d", "e");
    List<String> filtered = al.stream()
            .filter(s -> !s.equals("d"))
            .collect(Collectors.toList());
    System.out.println(filtered);
}

使用这种方法,您显然可以获得新列表的额外开销,但相反,您可以获得可以将列表视为不可变的,这将在多线程环境中很好地工作。

相关内容

  • 没有找到相关文章

最新更新