从java.util中删除一个元素时抛出一个ConcurrentModificationException。在列表迭代?(复制)

时间:2021-05-21 21:19:39

This question already has an answer here:

这个问题已经有了答案:

@Test
public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }

    for(String st:li){
        if(st.equalsIgnoreCase("str3"))
            li.remove("str3");
    }
    System.out.println(li);
}

When I run this code,I will throw a ConcurrentModificationException.

运行此代码时,将抛出ConcurrentModificationException。

It looks as though when I remove the specified element from the list,the list does not know its size have been changed.

看起来当我从列表中删除指定的元素时,列表不知道它的大小已经更改。

I'm wondering if this is a common problem with collections and removing elements?

我想知道这是否是集合和移除元素的常见问题?

11 个解决方案

#1


82  

I believe this is the purpose behind the Iterator.remove() method, to be able to remove an element from the collection while iterating.

我认为这是Iterator.remove()方法背后的目的,可以在迭代时从集合中删除元素。

For example:

例如:

Iterator<String> iter = li.iterator();
while(iter.hasNext()){
    if(iter.next().equalsIgnoreCase("str3"))
        iter.remove();
}

#2


16  

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will thow this exception

注意,这个异常并不总是表明一个对象被一个不同的线程同时修改了。如果单个线程发出一系列违反对象契约的方法调用,则对象可能抛出此异常。例如,如果线程在使用故障快速迭代器遍历集合时直接修改集合,则迭代器将对此异常进行处理

Taken from http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

从http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

#3


15  

The Java 8 way to remove it from the List without Iterator is:

不使用迭代器从列表中删除它的Java 8方法是:

li.removeIf(<predicate>)

i.e.

即。

List<String> li = new ArrayList<String>();
// ...
li = li.removeIf(st -> !st.equalsIgnoreCase("str3"));

#4


6  

yes people run into it -- the problem is you can't modify the list while iterating over it. I have used 2 alternatives in the past:

是的,人们会遇到它——问题是你不能在迭代列表时修改它。我过去用过两种选择:

  1. You can keep track of the indexes of the items you want to remove, and then remove them after you are done iterating.
  2. 您可以跟踪要删除的项的索引,然后在迭代之后删除它们。
  3. Or you can copy all the ones you want to keep into a new list as you iterate, and then discard the old list when done.
  4. 或者,您可以在迭代时将希望保留的所有列表复制到新列表中,然后在执行时丢弃旧列表。

those options assume you have to iterate over the list to find the elements to remove -- useful in cases where the list elements are complex objects with properties you might test on.

这些选项假定您必须遍历列表以查找要删除的元素——在列表元素是具有可测试属性的复杂对象的情况下是有用的。

In your particular case, you dont even need to iterate, as you can just use removeAll. Look at the API here. There are also nifty methods like retainAll that discard everything that is not in the argument. You can use remove/retain-like methods whenever the objects in the list implement equals and hashcode properly. If you cannot rely on equals/hashcode to identify equality between instances in your app, you will have to do the removal yourself....

在您的特定情况下,您甚至不需要迭代,因为您可以使用removeAll。看看这里的API。也有一些很好的方法,比如保存所有不包含在参数中的东西。只要列表中的对象正确地实现了equals和hashcode,就可以使用remove/ retainlike方法。如果你不能依靠equals和hashcode识别实例之间的平等在你的应用,你将不得不取消自己....

#5


1  

I got this problem and I think the easier way is the same with the second way that hvgotcodes gave.

我得到了这个问题,我认为更简单的方法和hvgotcodes给出的第二种方法是一样的。

Or you can copy all the ones you want to keep into a new list as you iterate, and then discard the old list when done.

或者,您可以在迭代时将希望保留的所有列表复制到新列表中,然后在执行时丢弃旧列表。

@Test
public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }
    List<String> finalLi = new ArrayList<String>();
    for(String st:li){
        if(st.equalsIgnoreCase("str3")){
            // Do nothing
        } else {
            finalLi.add(st);
        }
    }
    System.out.println(finalLi);
}

#6


1  

ArrayList has field modCount - count of collection modifications

ArrayList具有字段modCount -收集修改的计数

When you invoke method iterator() creates new object Itr. It has field expectedModCount. expectedModCount field initialize by modCount value. When you invoke

当您调用方法iterator()时,将创建新的对象Itr。场expectedModCount。使用modCount值初始化待定的modCount字段。当您调用

li.remove("str3");

modCount increments. When do you try access to li via iterator checks that expectedModCount == modCount

modCount增量。您什么时候通过迭代器检查访问li的方法,期望modCount == modCount ?

and if it is false throws ConcurrentModificationException

如果是false,则抛出ConcurrentModificationException。

Hence if you get iterator and after collection modified - iterator is considered not valid and you cannot use it.

因此,如果您得到迭代器,并且在集合修改后-迭代器被认为是无效的,您不能使用它。

#7


1  

I think it is worth mentioning the Java 8 version

我认为值得一提的是Java 8版本

@Test
public void testListCur() {
    List<String> li = new ArrayList<String>();
    for (int i = 0; i < 10; i++) {
        li.add("str" + i);
    }

    li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList());

    System.out.println(li);
}

#8


0  

I looped a different way...

我换了一种方式……

public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }

    for(int i=0; i<li.size(); i++)
        if(li.get(i).equalsIgnoreCase("str3"))
            li.remove(i--);

    System.out.println(li);
}

#9


0  

You could make a copy of list you want to remove element from, directly in for-each loop. For me, that is the simpliest way. Something like this:

您可以从for-each循环中直接创建要从其中删除元素的列表副本。对我来说,这是最简单的方法。是这样的:

for (String stringIter : new ArrayList<String>(myList)) {
    myList.remove(itemToRemove);
}

Hope that will help you..

希望这对你有帮助。

#10


0  

I think that best answer is from bigdev.de, but i would like to add something to it(like if the item is removed from a list, maybe you would like to log that somewhere or something):

我认为最好的答案来自bigdev.de,但是我想要添加一些东西(比如如果这个项目从列表中删除了,也许你想把它记录在某个地方或什么地方):

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

list.removeIf(a -> {
                boolean condition = a.equalsIgnoreCase("some condition");
                if(condition)
                    logger.info("Item removed from the list: " + a);
                return condition;
  });

#11


0  

Try this (Java 8):

试试这个(Java 8):

list.removeIf(condition);

#1


82  

I believe this is the purpose behind the Iterator.remove() method, to be able to remove an element from the collection while iterating.

我认为这是Iterator.remove()方法背后的目的,可以在迭代时从集合中删除元素。

For example:

例如:

Iterator<String> iter = li.iterator();
while(iter.hasNext()){
    if(iter.next().equalsIgnoreCase("str3"))
        iter.remove();
}

#2


16  

Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will thow this exception

注意,这个异常并不总是表明一个对象被一个不同的线程同时修改了。如果单个线程发出一系列违反对象契约的方法调用,则对象可能抛出此异常。例如,如果线程在使用故障快速迭代器遍历集合时直接修改集合,则迭代器将对此异常进行处理

Taken from http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

从http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

#3


15  

The Java 8 way to remove it from the List without Iterator is:

不使用迭代器从列表中删除它的Java 8方法是:

li.removeIf(<predicate>)

i.e.

即。

List<String> li = new ArrayList<String>();
// ...
li = li.removeIf(st -> !st.equalsIgnoreCase("str3"));

#4


6  

yes people run into it -- the problem is you can't modify the list while iterating over it. I have used 2 alternatives in the past:

是的,人们会遇到它——问题是你不能在迭代列表时修改它。我过去用过两种选择:

  1. You can keep track of the indexes of the items you want to remove, and then remove them after you are done iterating.
  2. 您可以跟踪要删除的项的索引,然后在迭代之后删除它们。
  3. Or you can copy all the ones you want to keep into a new list as you iterate, and then discard the old list when done.
  4. 或者,您可以在迭代时将希望保留的所有列表复制到新列表中,然后在执行时丢弃旧列表。

those options assume you have to iterate over the list to find the elements to remove -- useful in cases where the list elements are complex objects with properties you might test on.

这些选项假定您必须遍历列表以查找要删除的元素——在列表元素是具有可测试属性的复杂对象的情况下是有用的。

In your particular case, you dont even need to iterate, as you can just use removeAll. Look at the API here. There are also nifty methods like retainAll that discard everything that is not in the argument. You can use remove/retain-like methods whenever the objects in the list implement equals and hashcode properly. If you cannot rely on equals/hashcode to identify equality between instances in your app, you will have to do the removal yourself....

在您的特定情况下,您甚至不需要迭代,因为您可以使用removeAll。看看这里的API。也有一些很好的方法,比如保存所有不包含在参数中的东西。只要列表中的对象正确地实现了equals和hashcode,就可以使用remove/ retainlike方法。如果你不能依靠equals和hashcode识别实例之间的平等在你的应用,你将不得不取消自己....

#5


1  

I got this problem and I think the easier way is the same with the second way that hvgotcodes gave.

我得到了这个问题,我认为更简单的方法和hvgotcodes给出的第二种方法是一样的。

Or you can copy all the ones you want to keep into a new list as you iterate, and then discard the old list when done.

或者,您可以在迭代时将希望保留的所有列表复制到新列表中,然后在执行时丢弃旧列表。

@Test
public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }
    List<String> finalLi = new ArrayList<String>();
    for(String st:li){
        if(st.equalsIgnoreCase("str3")){
            // Do nothing
        } else {
            finalLi.add(st);
        }
    }
    System.out.println(finalLi);
}

#6


1  

ArrayList has field modCount - count of collection modifications

ArrayList具有字段modCount -收集修改的计数

When you invoke method iterator() creates new object Itr. It has field expectedModCount. expectedModCount field initialize by modCount value. When you invoke

当您调用方法iterator()时,将创建新的对象Itr。场expectedModCount。使用modCount值初始化待定的modCount字段。当您调用

li.remove("str3");

modCount increments. When do you try access to li via iterator checks that expectedModCount == modCount

modCount增量。您什么时候通过迭代器检查访问li的方法,期望modCount == modCount ?

and if it is false throws ConcurrentModificationException

如果是false,则抛出ConcurrentModificationException。

Hence if you get iterator and after collection modified - iterator is considered not valid and you cannot use it.

因此,如果您得到迭代器,并且在集合修改后-迭代器被认为是无效的,您不能使用它。

#7


1  

I think it is worth mentioning the Java 8 version

我认为值得一提的是Java 8版本

@Test
public void testListCur() {
    List<String> li = new ArrayList<String>();
    for (int i = 0; i < 10; i++) {
        li.add("str" + i);
    }

    li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList());

    System.out.println(li);
}

#8


0  

I looped a different way...

我换了一种方式……

public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }

    for(int i=0; i<li.size(); i++)
        if(li.get(i).equalsIgnoreCase("str3"))
            li.remove(i--);

    System.out.println(li);
}

#9


0  

You could make a copy of list you want to remove element from, directly in for-each loop. For me, that is the simpliest way. Something like this:

您可以从for-each循环中直接创建要从其中删除元素的列表副本。对我来说,这是最简单的方法。是这样的:

for (String stringIter : new ArrayList<String>(myList)) {
    myList.remove(itemToRemove);
}

Hope that will help you..

希望这对你有帮助。

#10


0  

I think that best answer is from bigdev.de, but i would like to add something to it(like if the item is removed from a list, maybe you would like to log that somewhere or something):

我认为最好的答案来自bigdev.de,但是我想要添加一些东西(比如如果这个项目从列表中删除了,也许你想把它记录在某个地方或什么地方):

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

list.removeIf(a -> {
                boolean condition = a.equalsIgnoreCase("some condition");
                if(condition)
                    logger.info("Item removed from the list: " + a);
                return condition;
  });

#11


0  

Try this (Java 8):

试试这个(Java 8):

list.removeIf(condition);