如何避免java.util.ConcurrentModificationException遍历和从数组列表中删除元素

我有一个数组列表,我想对它进行迭代。在迭代它时,我必须同时删除元素。显然,这会抛出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();

481190 次浏览

两个选择:

  • 创建一个你想要删除的值列表,在循环中添加到该列表,然后在结束时调用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();
}
}

从数组列表的JavaDocs

该类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在任何地方被结构修改 创建迭代器后的时间,除通过 迭代器自己的remove或add方法时,迭代器将抛出一个 ConcurrentModificationException。< / p >

一个选项是将removeA方法修改为this -

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

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

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

然后,迭代完成后,只需从第一个列表中的所有元素中执行removeAll

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

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

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

这是一个例子,我使用一个不同的列表来添加对象删除,然后我使用流。Foreach从原始列表中删除元素:

private ObservableList<CustomerTableEntry> customersTableViewItems = FXCollections.observableArrayList();
...
private void removeOutdatedRowsElementsFromCustomerView()
{
ObjectProperty<TimeStamp> currentTimestamp = new SimpleObjectProperty<>(TimeStamp.getCurrentTime());
long diff;
long diffSeconds;
List<Object> objectsToRemove = new ArrayList<>();
for(CustomerTableEntry item: customersTableViewItems) {
diff = currentTimestamp.getValue().getTime() - item.timestamp.getValue().getTime();
diffSeconds = diff / 1000 % 60;
if(diffSeconds > 10) {
// Element has been idle for too long, meaning no communication, hence remove it
System.out.printf("- Idle element [%s] - will be removed\n", item.getUserName());
objectsToRemove.add(item);
}
}
objectsToRemove.stream().forEach(o -> customersTableViewItems.remove(o));
}
你试图在高级的“for循环”中从列表中删除值,这是不可能的,即使你应用了任何技巧(在你的代码中)。 更好的方法是像这里建议的那样对迭代器级别进行编码

我想知道为什么人们没有建议传统的for循环方法。

for( int i = 0; i < lStringList.size(); i++ )
{
String lValue = lStringList.get( i );
if(lValue.equals("_Not_Required"))
{
lStringList.remove(lValue);
i--;
}
}

这也同样有效。

做一些简单的事情,像这样:

for (Object object: (ArrayList<String>) list.clone()) {
list.remove(object);
}

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

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

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

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

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

更多信息可以找到在这里

以正常的方式执行循环,java.util.ConcurrentModificationException是一个与所访问的元素相关的错误。

所以尝试:

for(int i = 0; i < list.size(); i++){
lista.get(i).action();
}

使用流的Java 8解决方案:

        theList = theList.stream()
.filter(element -> !shouldBeRemoved(element))
.collect(Collectors.toList());

在Java 7中,你可以使用Guava:

        theList = FluentIterable.from(theList)
.filter(new Predicate<String>() {
@Override
public boolean apply(String element) {
return !shouldBeRemoved(element);
}
})
.toImmutableList();

注意,Guava示例的结果是一个不可变列表,这可能是也可能不是您想要的。

在迭代列表时,如果您想删除元素是可能的。让我们看看下面的例子,

ArrayList<String>  names = new ArrayList<String>();
names.add("abc");
names.add("def");
names.add("ghi");
names.add("xyz");

我有上述名称的数组列表。我想从上面的列表中删除“def”名称,

for(String name : names){
if(name.equals("def")){
names.remove("def");
}
}

上面的代码抛出并发修改异常异常,因为你在迭代时修改列表。

因此,要从数组列表中删除def名称,

Iterator<String> itr = names.iterator();
while(itr.hasNext()){
String name = itr.next();
if(name.equals("def")){
itr.remove();
}
}

上面的代码,通过迭代器,我们可以从数组列表中删除“def”名称,并尝试打印数组,你会看到下面的输出。

输出:[abc, ghi, xyz]

使用普通的For循环而不是For循环。例如,下面的代码删除了数组列表中的所有元素,而没有给出java.util.ConcurrentModificationException。您可以根据您的用例修改循环中的条件。

for(int i=0; i<abc.size(); i++)  {
e.remove(i);
}

如果你的目标是从列表中删除所有元素,你可以遍历每一项,然后调用:

list.clear()

我知道我迟到了,但我回答这个问题,因为我认为这个解决方案简单而优雅:

List<String> listFixed = new ArrayList<String>();
List<String> dynamicList = new ArrayList<String>();


public void fillingList() {
listFixed.add("Andrea");
listFixed.add("Susana");
listFixed.add("Oscar");
listFixed.add("Valeria");
listFixed.add("Kathy");
listFixed.add("Laura");
listFixed.add("Ana");
listFixed.add("Becker");
listFixed.add("Abraham");
dynamicList.addAll(listFixed);
}


public void updatingListFixed() {
for (String newList : dynamicList) {
if (!listFixed.contains(newList)) {
listFixed.add(newList);
}
}


//this is for add elements if you want eraser also


String removeRegister="";
for (String fixedList : listFixed) {
if (!dynamicList.contains(fixedList)) {
removeResgister = fixedList;
}
}
fixedList.remove(removeRegister);
}
所有这些都是为了从一个列表更新到另一个列表,你可以从一个列表中进行所有更新 而在方法更新中,你既可以检查列表,也可以在列表之间删除或添加元素。 这意味着两个列表总是相同的大小

只要在你的ArrayList.remove (A)语句后添加一个打破

使用迭代器而不是数组列表

有一个集合被转换为类型匹配的迭代器

然后移动到下一个元素并删除

Iterator<Insured> itr = insuredSet.iterator();
while (itr.hasNext()) {
itr.next();
itr.remove();
}

移动到下一个元素在这里很重要,因为它应该使用索引来删除元素。

你也可以使用CopyOnWriteArrayList来代替ArrayList。这是JDK 1.5以后推荐的最新方法。

在我的情况下,接受的答案是不工作的,它停止异常,但它导致一些不一致在我的列表。下面的解决方案非常适合我。

List<String> list = new ArrayList<>();
List<String> itemsToRemove = new ArrayList<>();


for (String value: list) {
if (value.length() > 5) { // your condition
itemsToRemove.add(value);
}
}
list.removeAll(itemsToRemove);

在这段代码中,我在另一个列表中添加了要删除的项,然后使用list.removeAll方法删除所有必需的项。

关于

import java.util.Collections;


List<A> abc = Collections.synchronizedList(new ArrayList<>());

错误

有一个错误,当我添加到相同的列表从我获取元素:

fun <T> MutableList<T>.mathList(_fun: (T) -> T): MutableList<T> {
for (i in this) {
this.add(_fun(i))   <---   ERROR
}
return this   <--- ERROR
}

决定

工作时添加到一个新的列表:

fun <T> MutableList<T>.mathList(_fun: (T) -> T): MutableList<T> {
val newList = mutableListOf<T>()   <---   DECISION
for (i in this) {
newList.add(_fun(i))   <---   DECISION
}
return newList   <---   DECISION
}
for (A a : new ArrayList<>(abc)) {
a.doSomething();
abc.remove(a);
}

有时候守旧是最好的。只需要一个简单的for循环,但要确保从列表的末尾开始,否则当你删除项目时,你会与索引不同步。

List<String> list = new ArrayList<>();
for (int i = list.size() - 1; i >= 0; i--) {
if ("removeMe".equals(list.get(i))) {
list.remove(i);
}
}
List<String> list1 = new ArrayList<>();
list1.addAll(OriginalList);


List<String> list2 = new ArrayList<>();
list2.addAll(OriginalList);

这也是一种选择。