简体   繁体   English

我可以按顺序使用多个 listIterator 来改变或删除 Java 中的 ArrayList 中的列表元素吗?

[英]Can I use many listIterators sequentially to mutate or remove list elements from an ArrayList in Java?

I am relying on list iterators to move through a list of characters.我依靠列表迭代器在字符列表中移动。 This is a single-threaded program and I use listIterator objects sequentially in 4 different methods.这是一个单线程程序,我以 4 种不同的方法顺序使用 listIterator 对象。 Each method has the same setup:每种方法都有相同的设置:

private void myMethod(ArrayList<Integer> input) {
    ListIterator<Integer> i = input.listIterator();
    while (i.hasNext()) {
        Integer in = i.next();
        if (in < 10)
            i.remove();
        else
            i.set(in*in); // because its lucky
    }
}

With this pattern, on the second iterator the following Exception is thrown:使用这种模式,在第二个迭代器上会抛出以下异常:

java.util.ConcurrentModificationException

However, looking at the javadocs I don't see this Exception in the Exceptions thrown nor do I see a method to close the iterator after I am done.但是,查看 javadocs 我没有在抛出的异常中看到这个异常,也没有看到完成后关闭迭代器的方法。 Am I using the listIterator incorrectly?我是否错误地使用了 listIterator? I have to iterate over the same ArrayList multiple times, each time conditionally removing or mutating each element.我必须多次迭代同一个 ArrayList ,每次有条件地删除或改变每个元素。 Maybe there is a better way to iterate over the ArrayList and this use-case is not best solved by a ListIterator.也许有更好的方法来迭代 ArrayList,而 ListIterator 不能最好地解决这个用例。

java docs for ListIterator ListIterator 的 java 文档

This is explained in the ArrayList javadoc, you are modifying the list with remove() and set() while using an Iterator :这在ArrayList javadoc 中有解释,您在使用Iterator时使用remove()set()修改列表:

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException .此类的iteratorlistIterator方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时候对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外,迭代器将抛出ConcurrentModificationException Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

It's hard to give diagnostic for a problem when the shown code clearly isn't the code that produced the exception, as it doesn't even compile.当显示的代码显然不是产生异常的代码时,很难对问题进行诊断,因为它甚至无法编译。 The remove method of Iterator doesn't take arguments and the set method is defined on ListIterator , but your code declares the variable i only as Iterator . Iteratorremove方法不采用 arguments 并且set方法在ListIterator上定义,但是您的代码仅将变量i声明为Iterator

A fixed version固定版本

private void myMethod(ArrayList<Integer> input) {
    ListIterator<Integer> i = input.listIterator();
    while (i.hasNext()) {
        Integer in = i.next();
        if (in < 10)
            i.remove();
        else
            i.set(in*in);
    }
}

would run without problems.将毫无问题地运行。 The answer to your general question is that each modification invalidates all existing iterators, except the one used to make the modification when you did use an iterator for the modification and not the collection interface directly.您的一般问题的答案是,每次修改都会使所有现有迭代器无效,除了当您使用迭代器进行修改而不是直接使用集合接口时用于进行修改的迭代器。

But in your code, there is only one iterator, which is only created and used for this one operation.但是在您的代码中,只有一个迭代器,它仅被创建并用于这一操作。 As long as there is no overlapping use of iterators to the same collection, there is no problem with the invalidation.只要对同一个集合没有重复使用迭代器,失效就没有问题。 Iterators existing from previous operations are abandoned anyway and the iterators used in subsequent operations do not exist yet.先前操作中存在的迭代器无论如何都会被放弃,并且后续操作中使用的迭代器还不存在。

Still, it's easier to use尽管如此,它更容易使用

private void myMethod(ArrayList<Integer> input) {
    input.removeIf(in -> in < 10);
    input.replaceAll(in -> in*in);
}

instead.反而。 Unlike the original code, this does two iterations, but as explained in this answer , removeIf will be actually faster than iterator based removal in those cases, where performance really matters.与原始代码不同,这进行了两次迭代,但正如在这个答案中所解释的那样,在那些性能真正重要的情况下, removeIf实际上会比基于迭代器的删除更快。

But still, the problem persists.但是,问题仍然存在。 The shown code can't cause a ConcurrentModificationException , so your actual problem is somewhere else and may still be present, regardless of how this one method has been implemented.显示的代码不会导致ConcurrentModificationException ,因此您的实际问题在其他地方并且可能仍然存在,无论这种方法是如何实现的。

I am not knowledgable enough about Java ListIterators to answer the question but it appears I have run into the XY problem here.我对 Java ListIterators 不够了解来回答这个问题,但似乎我在这里遇到了 XY 问题。 The problem seems to be better solved with Java Streams to remove the element or map the element into a new ArrayList by exercising a function on each element in the original ArrayList. The problem seems to be better solved with Java Streams to remove the element or map the element into a new ArrayList by exercising a function on each element in the original ArrayList.

    private ArrayList<Integer> myMethod(ArrayList<Integer> input) {
        ArrayList<Integer> results = input.stream().filter(
            in -> (in < 10)).collect(Collectors.toCollection(ArrayList::new));

        results = input.stream().map(
            in -> in*in).collect(Collectors.toCollection(ArrayList::new));

        return results;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM