从列表中动态删除元素

5

我在遍历列表时,无法删除其中的元素。代码:

For (WebElement element: list){
    if (!element.isEnabled() || !element.isSelected()){
        list.remove(element);
    }
}

我遇到了一个ConcurrentModificationException,我完全理解这个问题。我在遍历列表的循环中删除一个项目。直觉上,那会搞乱循环的索引。
我的问题是,我应该如何从这个列表中删除未启用或未选定的元素?
4个回答

8

在循环中删除列表元素的最简单方法是使用 ListIterator 并使用例程 iterator.remove() 删除元素。


我不确定这是否是最简单的方法。remove()Iterator<E> 接口上的一个可选功能。值得注意的是,remove()Iterator<E> 上,并且仅被 ListIterator<E> 继承。 - corsiKa

8

在不使用迭代器的情况下修改正在遍历的列表会导致未定义的行为。你必须明确使用迭代器:

Iterator<WebElement> iter = list.iterator();
while (iter.hasNext()) {
    WebElement element = iter.next();
    if (!element.isEnabled() || !element.isSelected()) {
        iter.remove();
    }
}

请参见这个问题以了解更多信息。


嗯,我明白了。那么如果我想把 Iterator 转换回 List,除了在循环中逐个添加每个元素外,是否有更简单的方法? - jamesfzhang
2
这并不会将 List 转换为 Iterator - Iterator 只是一个作用于该列表本身的对象 - 它是用于遍历列表的接口。当您调用 iter.remove() 时,它确实修改了底层列表。 - Claudiu

3

有人建议使用列表迭代器。这对我很有用,但不幸的是,它依赖于一个方法remove(),这个方法被Iterable<E>接口认为是可选的。

Javadoc中写道(重点在此):

void remove()

从基础集合中删除迭代器返回的最后一个元素(可选操作)

为了避免这个问题,我发现使用一个移除列表更加有用。

List<E> removed = new ArrayList<E>();
for(E element : list) {
    if(someCondition) removed.add(element);
}
list.removeAll(removed);

这样做的好处是,你可以像使用remove方法一样获得所删除内容的历史记录。

我非常喜欢这个 (+1)。然而,元素 E 应该有一个适当的 equals 方法重写。 - GETah
1
@GETah 不一定需要。即使没有它,它也可以正常工作,甚至可能更好。您可以从工厂方法中获取它们,在这种情况下,您对等式方法的需求要少得多,严格依赖于引用来实现相等性。 - corsiKa
1
哇,我也非常喜欢这个!非常出色的创新思维。 - jamesfzhang
更新后包括我为什么更喜欢这种方法而不是迭代器上的“remove”的原因。 - corsiKa

0

ConcurrentModificationException 是由于 for-each 语法只是使用 Iterator 接口的语法糖而导致的。

列表迭代器具有所谓的“快速失败”属性,这意味着除了迭代器提供的接口之外对列表所做的任何更改都会立即使该迭代器无效。尝试使用无效的迭代器会触发异常。

@Claudiu 已经发布了此代码,但为了清晰起见,我也将其放在这里。为了实现您想要的功能,您必须放弃花哨的语法并使用裸 Iterator。

Iterator<WebElement iter = list.iterator();
while (iter.hasNext()) {
    WebElement element = iter.next();
    if (!element.isEnabled() || !element.isSelected()) {
        iter.remove();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接