我有一个数组列表,我想对它进行迭代。在迭代它时,我必须同时删除元素。显然,这会抛出一个java.util.ConcurrentModificationException。

处理这个问题的最佳实践是什么?我应该先克隆列表吗?

我删除的元素不是循环本身,而是代码的另一部分。

我的代码是这样的:

public class Test() {
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff() {
        for (A a : abc) 
        a.doSomething();
    }

    public void removeA(A a) {
        abc.remove(a);
    }
}

a.doSomething可能调用Test.removeA();


当前回答

你应该用传统的方式迭代数组

每次从列表中删除一个元素,之后的元素都会向前推。只要不改变迭代元素以外的元素,下面的代码就可以工作。

public class Test(){
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff(){
        for(int i = (abc.size() - 1); i >= 0; i--) 
            abc.get(i).doSomething();
    }

    public void removeA(A a){
        abc.remove(a);
    }
}

其他回答

两个选择:

创建一个您希望删除的值列表,在循环中添加到该列表中,然后在结束时调用originalList.removeAll(valuesToRemove) 对迭代器本身使用remove()方法。注意,这意味着您不能使用增强的for循环。

作为第二个选项的示例,从列表中删除长度大于5的任何字符串:

List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String value = iterator.next();
    if (value.length() > 5) {
        iterator.remove();
    }
}

在Java 8中,你可以通过调用removeIf方法来使用Collection Interface:

yourList.removeIf((A a) -> a.value == 2);

更多信息可以在这里找到

你应该用传统的方式迭代数组

每次从列表中删除一个元素,之后的元素都会向前推。只要不改变迭代元素以外的元素,下面的代码就可以工作。

public class Test(){
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff(){
        for(int i = (abc.size() - 1); i >= 0; i--) 
            abc.get(i).doSomething();
    }

    public void removeA(A a){
        abc.remove(a);
    }
}

“我应该先克隆列表吗?”

这将是最简单的解决方案,从克隆中删除,并在删除后将克隆复制回来。

以我的rummikub游戏为例:

SuppressWarnings("unchecked")
public void removeStones() {
  ArrayList<Stone> clone = (ArrayList<Stone>) stones.clone();
  // remove the stones moved to the table
  for (Stone stone : stones) {
      if (stone.isOnTable()) {
         clone.remove(stone);
      }
  }
  stones = (ArrayList<Stone>) clone.clone();
  sortStones();
}

一种选择是将removeA方法修改为this -

public void removeA(A a,Iterator<A> iterator) {
     iterator.remove(a);
     }

但这意味着doSomething()应该能够将迭代器传递给remove方法。这不是个好主意。

你能分两步做到吗? 在第一个循环中,当遍历列表时,不是删除所选元素,而是将它们标记为要删除。为此,您可以简单地将这些元素(浅复制)复制到另一个List中。

然后,迭代完成后,只需从第一个列表中删除第二个列表中的所有元素。