在Java中,我可以按顺序使用多个listIterator来从ArrayList中突变或删除列表元素吗



我依靠列表迭代器来遍历字符列表。这是一个单线程程序,我用4种不同的方法依次使用listIterator对象。每种方法都有相同的设置:

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
}
}

使用这种模式,在第二个迭代器上抛出以下异常:

java.util.ConcurrentModificationException

然而,在javadocs中,我没有在抛出的Exception中看到这个Exception,也没有看到在完成后关闭迭代器的方法。我是否错误地使用了listIterator?我必须对同一个ArrayList进行多次迭代,每次都有条件地移除或更改每个元素。也许有一种更好的方法来迭代ArrayList,而这个用例并不是通过ListIterator来最好地解决的。

ListIterator 的java文档

这在ArrayListjavadoc中有解释,您在使用Iterator:时使用remove()set()修改列表

该类的iteratorlistIterator方法返回的迭代器是故障快速的:如果在迭代器创建后的任何时候对列表进行结构修改,则迭代器将抛出一个ConcurrentModificationException,除非通过迭代器自己的remove或add方法。因此,面对并发修改,迭代器会快速而干净地失败,而不是冒着在未来不确定的时间出现任意、不确定行为的风险。

当显示的代码显然不是产生异常的代码时,很难对问题进行诊断,因为它甚至没有编译。Iteratorremove方法不接受参数,set方法是在ListIterator上定义的,但您的代码仅将变量i声明为Iterator

固定版本

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

运行时不会出现问题。一般问题的答案是,每次修改都会使所有现有的迭代器失效,但当您使用迭代器而不是直接使用集合接口进行修改时,用于进行修改的迭代程序除外。

但在您的代码中,只有一个迭代器,它只被创建并用于这一操作。只要迭代器对同一集合的使用不重叠,就不会有无效的问题。先前操作中存在的迭代器无论如何都会被放弃,并且后续操作中使用的迭代程序还不存在。

不过,使用更容易

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

相反。与原始代码不同,这会进行两次迭代,但正如本答案中所解释的,在性能真正重要的情况下,removeIf实际上会比基于迭代器的删除更快。

但问题依然存在。所示的代码不会导致ConcurrentModificationException,因此无论这一方法是如何实现的,您的实际问题都在其他地方,并且可能仍然存在。

我对Java ListIterator的了解不够,无法回答这个问题,但我在这里似乎遇到了XY问题。Java Streams通过对原始ArrayList中的每个元素执行一个函数来移除元素或将元素映射到新的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;
}

相关内容

最新更新