通过集合进行迭代,避免在循环中删除对象时进行contrentModification Exception

发布于 2025-02-13 13:31:47 字数 857 浏览 0 评论 0 原文

我们都知道,由于 conturrentModificationException

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

但这显然有时可以,但并非总是如此。这是一些特定的代码:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

当然,这会导致:

Exception in thread "main" java.util.ConcurrentModificationException

即使多个线程没有这样做。反正。

解决这个问题的最佳解决方案是什么?如何在不抛出此例外的情况下以循环中的循环中删除一个项目?

我还在这里使用任意收集,不一定是 arrayList ,因此您不能依靠 get

We all know you can't do the following because of ConcurrentModificationException:

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

But this apparently works sometimes, but not always. Here's some specific code:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

This, of course, results in:

Exception in thread "main" java.util.ConcurrentModificationException

Even though multiple threads aren't doing it. Anyway.

What's the best solution to this problem? How can I remove an item from the collection in a loop without throwing this exception?

I'm also using an arbitrary Collection here, not necessarily an ArrayList, so you can't rely on get.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

扮仙女 2025-02-20 13:31:47

iterator.remove()< /code> 是安全的,您可以这样使用它:

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

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

请注意 iterator.remove() 是在迭代过程中修改收集的唯一安全方法;如果在进行迭代进行时,以任何其他方式修改了基础集合,则该行为是未指定的。

资料来源: docs.oracle&gt;集合接口


类似地,如果您有 ListIterator 并想要 add 项目,则可以使用 listiterator#add ,出于相同的原因,您可以使用 Iterator#remove &nbsp; - 它旨在允许它。


在您的情况下,您尝试从列表中删除,但是如果尝试 put 添加到地图时,则适用相同的限制。

Iterator.remove() is safe, you can use it like this:

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

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

Note that Iterator.remove() is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.

Source: docs.oracle > The Collection Interface


And similarly, if you have a ListIterator and want to add items, you can use ListIterator#add, for the same reason you can use Iterator#remove — it's designed to allow it.


In your case you tried to remove from a list, but the same restriction applies if trying to put into a Map while iterating its content.

凉薄对峙 2025-02-20 13:31:47

这起作用了:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

我假设,由于foreach循环是用于迭代的句法糖,因此使用迭代器无济于事...但是它为您提供了此 .remove()函数。

This works:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

I assumed that since a foreach loop is syntactic sugar for iterating, using an iterator wouldn't help... but it gives you this .remove() functionality.

旧话新听 2025-02-20 13:31:47

使用Java 8,您可以使用新的 remove 方法。应用于您的示例:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

一个简单的测试作为示例:

    @Test
    public void testRemoveIfOneList() {
        List<String> outer = new ArrayList<>();
        outer.add("one");
        outer.add("two");
        outer.add("three");

        outer.removeIf(o -> o.length() == 3);

        assertEquals(1, outer.size());
    }

当您比较两个列表并希望从两者中删除时,它甚至可以工作。

    @Test
    public void testRemoveIfTwoLists() {
        List<String> outer = new ArrayList<>();
        outer.add("one");
        outer.add("two");
        outer.add("three");
        List<String> inner = new ArrayList<>();
        inner.addAll(outer);

        // first, it removes from inner, and if anything is removed, then removeIf() returns true,
        // leading to removing from outer
        outer.removeIf(o -> inner.removeIf(i -> i.equals(o)));

        assertEquals(0, outer.size());
        assertEquals(0, inner.size());
    }

但是,如果其中一个具有重复项,请确保它在内部循环中迭代,因为对于内部列表,它将删除所有符合条件的 elements 元素,但对于外部列表,当删除任何元素时,它将立即返回并停止检查。

该测试将失败:

    @Test
    public void testRemoveIfTwoListsInnerHasDuplicates() {
        List<String> outer = new ArrayList<>();
        outer.add("one");
        outer.add("one");
        outer.add("two");
        outer.add("two");
        outer.add("three");
        outer.add("three");
        List<String> inner = new ArrayList<>();
        inner.addAll(outer); // both have duplicates

        // remove all elements from inner(executed twice), then remove from outer
        // but only once! if anything is removed, it will return immediately!!
        outer.removeIf(o -> inner.removeIf(i -> i.equals(o)));

        assertEquals(0, inner.size()); // pass, inner all removed
        assertEquals(0, outer.size()); // will fail, outer has size = 3
    }

With Java 8 you can use the new removeIf method. Applied to your example:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

A simple test as example:

    @Test
    public void testRemoveIfOneList() {
        List<String> outer = new ArrayList<>();
        outer.add("one");
        outer.add("two");
        outer.add("three");

        outer.removeIf(o -> o.length() == 3);

        assertEquals(1, outer.size());
    }

It even works when you compare two lists and want to remove from both.

    @Test
    public void testRemoveIfTwoLists() {
        List<String> outer = new ArrayList<>();
        outer.add("one");
        outer.add("two");
        outer.add("three");
        List<String> inner = new ArrayList<>();
        inner.addAll(outer);

        // first, it removes from inner, and if anything is removed, then removeIf() returns true,
        // leading to removing from outer
        outer.removeIf(o -> inner.removeIf(i -> i.equals(o)));

        assertEquals(0, outer.size());
        assertEquals(0, inner.size());
    }

However, if one of the list has duplicates, make sure it's iterated in the inner loop, because for inner list, it will remove all elements meeting the criteria, but for outer list, when any element is removed, it will return immediately and stops checking.

This test will fail:

    @Test
    public void testRemoveIfTwoListsInnerHasDuplicates() {
        List<String> outer = new ArrayList<>();
        outer.add("one");
        outer.add("one");
        outer.add("two");
        outer.add("two");
        outer.add("three");
        outer.add("three");
        List<String> inner = new ArrayList<>();
        inner.addAll(outer); // both have duplicates

        // remove all elements from inner(executed twice), then remove from outer
        // but only once! if anything is removed, it will return immediately!!
        outer.removeIf(o -> inner.removeIf(i -> i.equals(o)));

        assertEquals(0, inner.size()); // pass, inner all removed
        assertEquals(0, outer.size()); // will fail, outer has size = 3
    }
空心空情空意 2025-02-20 13:31:47

由于问题已经回答,即最好的方法是使用迭代对象的删除方法,因此我将进入错误“ java.util.concurrentmodification exception” 的详细信息。 。

每个集合类都有一个私人类,该类实现了迭代器接口,并提供了 next() remove() and hasnext() hasnext()之类的方法。

接下来的代码看起来像这样...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

这里的方法 CheckForcomodification

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

这样的,如您所见,如果您明确尝试从集合中删除元素,则可以看到。它导致 modCount greengemodcount 不同,导致异常 concurrentModificationException

Since the question has been already answered i.e. the best way is to use the remove method of the iterator object, I would go into the specifics of the place where the error "java.util.ConcurrentModificationException" is thrown.

Every collection class has a private class which implements the Iterator interface and provides methods like next(), remove() and hasNext().

The code for next looks something like this...

public E next() {
    checkForComodification();
    try {
        E next = get(cursor);
        lastRet = cursor++;
        return next;
    } catch(IndexOutOfBoundsException e) {
        checkForComodification();
        throw new NoSuchElementException();
    }
}

Here the method checkForComodification is implemented as

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

So, as you can see, if you explicitly try to remove an element from the collection. It results in modCount getting different from expectedModCount, resulting in the exception ConcurrentModificationException.

浅忆 2025-02-20 13:31:47

您可以像提到的那样直接使用迭代器,也可以保留第二个集合,然后将要删除的每个项目添加到新集合中,然后在末尾删除。这使您可以继续以增加内存使用时间和CPU时间为代价来使用for-EAK循环的类型安全(除非您确实有真正,很大的列表或真正旧的计算机,否则不应该是一个巨大的问题)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}

You can either use the iterator directly like you mentioned, or else keep a second collection and add each item you want to remove to the new collection, then removeAll at the end. This allows you to keep using the type-safety of the for-each loop at the cost of increased memory use and cpu time (shouldn't be a huge problem unless you have really, really big lists or a really old computer)

public static void main(String[] args)
{
    Collection<Integer> l = new ArrayList<Integer>();
    Collection<Integer> itemsToRemove = new ArrayList<>();
    for (int i=0; i < 10; i++) {
        l.add(Integer.of(4));
        l.add(Integer.of(5));
        l.add(Integer.of(6));
    }
    for (Integer i : l)
    {
        if (i.intValue() == 5) {
            itemsToRemove.add(i);
        }
    }

    l.removeAll(itemsToRemove);
    System.out.println(l);
}
怂人 2025-02-20 13:31:47

情况下

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

在这种 >在流上。

In such cases a common trick is (was?) to go backwards:

for(int i = l.size() - 1; i >= 0; i --) {
  if (l.get(i) == 5) {
    l.remove(i);
  }
}

That said, I'm more than happy that you have better ways in Java 8, e.g. removeIf or filter on streams.

季末如歌 2025-02-20 13:31:47

claudius

for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
    Object object = it.next();
    if (test) {
        it.remove();
    }
}

Same answer as Claudius with a for loop:

for (Iterator<Object> it = objects.iterator(); it.hasNext();) {
    Object object = it.next();
    if (test) {
        it.remove();
    }
}
日记撕了你也走了 2025-02-20 13:31:47

制作现有列表的副本,并在新副本上进行迭代。

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}

Make a copy of existing list and iterate over new copy.

for (String str : new ArrayList<String>(listOfStr))     
{
    listOfStr.remove(/* object reference or index */);
}
月朦胧 2025-02-20 13:31:47

使用 eclipse collections ,方法 emove> remove> remove mutableCollection 将有效

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

: Java 8 lambda语法可以写如下:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

在此需要呼叫 predicates.cast.cast.cast(),因为在 Java上添加了默认的 remove> remove 方法。 util.Collection Java 8中的接口。

注意:我是 eclipse collections 。

With Eclipse Collections, the method removeIf defined on MutableCollection will work:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.lessThan(3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

With Java 8 Lambda syntax this can be written as follows:

MutableList<Integer> list = Lists.mutable.of(1, 2, 3, 4, 5);
list.removeIf(Predicates.cast(integer -> integer < 3));
Assert.assertEquals(Lists.mutable.of(3, 4, 5), list);

The call to Predicates.cast() is necessary here because a default removeIf method was added on the java.util.Collection interface in Java 8.

Note: I am a committer for Eclipse Collections.

掀纱窥君容 2025-02-20 13:31:47

人们断言一个不能从foreach循环迭代的集合中删除。我只是想指出,这是在技术上是不正确的,并确切地描述了(我知道OP的问题是如此先进,以至于可以避免知道这一点)该假设背后的代码:

for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
    if (obj.isTouched()) {
        untouchedSet.remove(obj);
        touchedSt.add(obj);
        break;  // this is key to avoiding returning to the foreach
    }
}

不是你不能从迭代 colletion 中删除,而是一旦执行,就无法继续迭代。因此,上面的代码中的中断

抱歉,如果这个答案是一个专业的用例,并且更适合原始的我从这里到达这里,这被标记为对此的重复(尽管看起来更细微)并被锁定。

People are asserting one can't remove from a Collection being iterated by a foreach loop. I just wanted to point out that is technically incorrect and describe exactly (I know the OP's question is so advanced as to obviate knowing this) the code behind that assumption:

for (TouchableObj obj : untouchedSet) {  // <--- This is where ConcurrentModificationException strikes
    if (obj.isTouched()) {
        untouchedSet.remove(obj);
        touchedSt.add(obj);
        break;  // this is key to avoiding returning to the foreach
    }
}

It isn't that you can't remove from the iterated Colletion rather that you can't then continue iteration once you do. Hence the break in the code above.

Apologies if this answer is a somewhat specialist use-case and more suited to the original thread I arrived here from, that one is marked as a duplicate (despite this thread appearing more nuanced) of this and locked.

打小就很酷 2025-02-20 13:31:47

带有传统的循环

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}

With a traditional for loop

ArrayList<String> myArray = new ArrayList<>();

for (int i = 0; i < myArray.size(); ) {
    String text = myArray.get(i);
    if (someCondition(text))
        myArray.remove(i);
    else
        i++;   
}
心碎的声音 2025-02-20 13:31:47

concurrenthashmap href =“ https://docs.oracle.com/javase/7/docs/api/java/java/util/concurrent/concurrent/concurrentlinkedqueue.html” rel =“ noreferrer”> concurrentlinkedqueue href =“ https://docs.oracle.com/javase/7/docs/api/java/java/java/util/concurrent/concurrentskiplistmap.html” rel =“ noreferrer”> concurrentskiplistmap 可能是另一个选项即使您删除或添加项目,也切勿抛出任何contrentModification Exception。

ConcurrentHashMap or ConcurrentLinkedQueue or ConcurrentSkipListMap may be another option, because they will never throw any ConcurrentModificationException, even if you remove or add item.

-小熊_ 2025-02-20 13:31:47

另一种方法是将您的阵列列表的副本仅用于迭代:

List<Object> l = ...
    
List<Object> iterationList = ImmutableList.copyOf(l);
    
for (Object curr : iterationList) {
    if (condition(curr)) {
        l.remove(curr);
    }
}

Another way is to use a copy of your arrayList just for iteration:

List<Object> l = ...
    
List<Object> iterationList = ImmutableList.copyOf(l);
    
for (Object curr : iterationList) {
    if (condition(curr)) {
        l.remove(curr);
    }
}
顾冷 2025-02-20 13:31:47

Listiterator 允许您在列表中添加或删除项目。假设您有 car 对象的列表:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}

A ListIterator allows you to add or remove items in the list. Suppose you have a list of Car objects:

List<Car> cars = ArrayList<>();
// add cars here...

for (ListIterator<Car> carIterator = cars.listIterator();  carIterator.hasNext(); )
{
   if (<some-condition>)
   { 
      carIterator().remove()
   }
   else if (<some-other-condition>)
   { 
      carIterator().add(aNewCar);
   }
}
锦欢 2025-02-20 13:31:47

现在,您可以使用以下代码删除

l.removeIf(current -> current == 5);

Now, You can remove with the following code

l.removeIf(current -> current == 5);
李白 2025-02-20 13:31:47

我知道这个问题太老了,无法与Java 8有关,但是对于使用Java 8的人,您可以轻松地使用Removeif():

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);

I know this question is too old to be about Java 8, but for those using Java 8 you can easily use removeIf():

Collection<Integer> l = new ArrayList<Integer>();

for (int i=0; i < 10; ++i) {
    l.add(new Integer(4));
    l.add(new Integer(5));
    l.add(new Integer(6));
}

l.removeIf(i -> i.intValue() == 5);
似最初 2025-02-20 13:31:47

Java并发修改异常

  1. 单线程
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        list.remove(it.next()); //throws ConcurrentModificationException
    }
}

解决方案:迭代器 remove() Method

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        it.remove()
    }
}
  1. Muthi thread
  • copy/convert/tock and Convert and tock yt Its在另一个集合上进行迭代。对于小型收藏
  • <代码>同步 [关于]
  • 线程安全收藏href =“ https://stackoverflow.com/a/a/65410514/47770877”> [关于]

Java Concurrent Modification Exception

  1. Single thread
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        list.remove(it.next()); //throws ConcurrentModificationException
    }
}

Solution: iterator remove() method

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String value = iter.next()
    if (value == "A") {
        it.remove()
    }
}
  1. Multi thread
  • copy/convert and iterate over another one collection. For small collections
  • synchronize[About]
  • thread safe collection[About]
独留℉清风醉 2025-02-20 13:31:47

我对上述问题有一个建议。无需辅助列表或任何额外的时间。请找到一个可以做同样的事情但以不同方式进行相同工作的示例。

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

这将避免并发例外。

I have a suggestion for the problem above. No need of secondary list or any extra time. Please find an example which would do the same stuff but in a different way.

//"list" is ArrayList<Object>
//"state" is some boolean variable, which when set to true, Object will be removed from the list
int index = 0;
while(index < list.size()) {
    Object r = list.get(index);
    if( state ) {
        list.remove(index);
        index = 0;
        continue;
    }
    index += 1;
}

This would avoid the Concurrency Exception.

蓝戈者 2025-02-20 13:31:47
for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

如果您跳过内部迭代仪()调用,则捕获量是在后面将元素从列表中删除。它仍然有效!尽管我不建议这样写这样的代码,但它有助于理解其背后的概念:-)

欢呼!

for (Integer i : l)
{
    if (i.intValue() == 5){
            itemsToRemove.add(i);
            break;
    }
}

The catch is the after removing the element from the list if you skip the internal iterator.next() call. it still works! Though I dont propose to write code like this it helps to understand the concept behind it :-)

Cheers!

霓裳挽歌倾城醉 2025-02-20 13:31:47

线程安全收集修改的示例:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}

Example of thread safe collection modification:

public class Example {
    private final List<String> queue = Collections.synchronizedList(new ArrayList<String>());

    public void removeFromQueue() {
        synchronized (queue) {
            Iterator<String> iterator = queue.iterator();
            String string = iterator.next();
            if (string.isEmpty()) {
                iterator.remove();
            }
        }
    }
}
温柔戏命师 2025-02-20 13:31:47

我知道这个问题仅假定一个集合,而不是更具体地说明任何 list 。但是对于那些确实正在使用 list 参考的人来说在其中进行修改),而不是要避免迭代器 (如果您想避免使用它,或者特别避免使用它以达到与启动到结束停止不同的循环顺序每个元素[我认为这是唯一的订单迭代器本身可以做到]):

*更新:请参阅下面的注释,可以使用传统 - 也可以实现类似的内容。 for-loop。

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

no conturrentModification exception从该代码中。

我们看到循环不是从开始的开始,而不是在 中停止迭代器本身无法做到)。

fwiw我们还可以在 list 上看到 get ,如果其引用仅为 collection (而不是更具体的 list -type的收集) - list 接口包括 get get ,但 Collection 接口>接口不知道。如果不是为了这种差异,那么 list 参考可以是 collection [因此,从技术上讲,此答案将是直接答案,而不是切向答案]。

FWIWW相同的代码在修改后仍然可以在每个元素的stop stop中开始工作(就像 Iterator 订单一样):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}

I know this question assumes just a Collection, and not more specifically any List. But for those reading this question who are indeed working with a List reference, you can avoid ConcurrentModificationException with a while-loop (while modifying within it) instead if you want to avoid Iterator (either if you want to avoid it in general, or avoid it specifically to achieve a looping order different from start-to-end stopping at each element [which I believe is the only order Iterator itself can do]):

*Update: See comments below that clarify the analogous is also achievable with the traditional-for-loop.

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 1;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i++);

    } else {
        i += 2;
    }
}

No ConcurrentModificationException from that code.

There we see looping not start at the beginning, and not stop at every element (which I believe Iterator itself can't do).

FWIW we also see get being called on list, which could not be done if its reference was just Collection (instead of the more specific List-type of Collection) - List interface includes get, but Collection interface does not. If not for that difference, then the list reference could instead be a Collection [and therefore technically this Answer would then be a direct Answer, instead of a tangential Answer].

FWIWW same code still works after modified to start at beginning at stop at every element (just like Iterator order):

final List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; ++i){
    list.add(i);
}

int i = 0;
while(i < list.size()){
    if(list.get(i) % 2 == 0){
        list.remove(i);

    } else {
        ++i;
    }
}
内心荒芜 2025-02-20 13:31:47

一种解决方案可能是旋转列表并删除第一个元素,以避免conturrentModificationException或indexoutofBoundSexception

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);

One solution could be to rotate the list and remove the first element to avoid the ConcurrentModificationException or IndexOutOfBoundsException

int n = list.size();
for(int j=0;j<n;j++){
    //you can also put a condition before remove
    list.remove(0);
    Collections.rotate(list, 1);
}
Collections.rotate(list, -1);
迷乱花海 2025-02-20 13:31:47

尝试这个(删除等于 i 的列表中的所有元素):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}

Try this one (removes all elements in the list that equal i):

for (Object i : l) {
    if (condition(i)) {
        l = (l.stream().filter((a) -> a != i)).collect(Collectors.toList());
    }
}
不奢求什么 2025-02-20 13:31:47

您可以使用一个时循环。

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if(entry.getKey().equals("test")) {
        iterator.remove();
    } 
}

You can use a while loop.

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, String> entry = iterator.next();
    if(entry.getKey().equals("test")) {
        iterator.remove();
    } 
}
樱娆 2025-02-20 13:31:47

我最终使用了此 consurrentModificationException ,同时使用 stream()。map()方法迭代列表。但是,(:)在迭代和修改列表时没有引发异常。

这是代码段,如果它对任何人提供帮助:
在这里,我在 arrayList&lt; buildentity&gt; 上迭代,并使用list.remove(obj)对其进行修改

 for(BuildEntity build : uniqueBuildEntities){
            if(build!=null){
                if(isBuildCrashedWithErrors(build)){
                    log.info("The following build crashed with errors ,  will not be persisted -> \n{}"
                            ,build.getBuildUrl());
                    uniqueBuildEntities.remove(build);
                    if (uniqueBuildEntities.isEmpty()) return  EMPTY_LIST;
                }
            }
        }
        if(uniqueBuildEntities.size()>0) {
            dbEntries.addAll(uniqueBuildEntities);
        }

I ended up with this ConcurrentModificationException, while iterating the list using stream().map() method. However the for(:) did not throw the exception while iterating and modifying the the list.

Here is code snippet , if its of help to anyone:
here I'm iterating on a ArrayList<BuildEntity> , and modifying it using the list.remove(obj)

 for(BuildEntity build : uniqueBuildEntities){
            if(build!=null){
                if(isBuildCrashedWithErrors(build)){
                    log.info("The following build crashed with errors ,  will not be persisted -> \n{}"
                            ,build.getBuildUrl());
                    uniqueBuildEntities.remove(build);
                    if (uniqueBuildEntities.isEmpty()) return  EMPTY_LIST;
                }
            }
        }
        if(uniqueBuildEntities.size()>0) {
            dbEntries.addAll(uniqueBuildEntities);
        }
烟织青萝梦 2025-02-20 13:31:47

如果使用hashmap,则在Java(8+)的较新版本中,您可以选择3个选项中的每个选项:

public class UserProfileEntity {
    private String Code;
    private String mobileNumber;
    private LocalDateTime inputDT;
    // getters and setters here
}
HashMap<String, UserProfileEntity> upMap = new HashMap<>();


// remove by value
upMap.values().removeIf(value -> !value.getCode().contains("0005"));

// remove by key
upMap.keySet().removeIf(key -> key.contentEquals("testUser"));

// remove by entry / key + value
upMap.entrySet().removeIf(entry -> (entry.getKey().endsWith("admin") || entry.getValue().getInputDT().isBefore(LocalDateTime.now().minusMinutes(3)));

If using HashMap, in newer versions of Java (8+) you can select each of 3 options:

public class UserProfileEntity {
    private String Code;
    private String mobileNumber;
    private LocalDateTime inputDT;
    // getters and setters here
}
HashMap<String, UserProfileEntity> upMap = new HashMap<>();


// remove by value
upMap.values().removeIf(value -> !value.getCode().contains("0005"));

// remove by key
upMap.keySet().removeIf(key -> key.contentEquals("testUser"));

// remove by entry / key + value
upMap.entrySet().removeIf(entry -> (entry.getKey().endsWith("admin") || entry.getValue().getInputDT().isBefore(LocalDateTime.now().minusMinutes(3)));
无敌元气妹 2025-02-20 13:31:47

最好的方法(推荐)是使用 java.util.concurrent 软件包。经过
使用此软件包,您可以轻松避免此例外。参考
修改的代码:

public static void main(String[] args) {
    Collection<Integer> l = new CopyOnWriteArrayList<Integer>();
    
    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }
    
    for (Integer i : l) {
        if (i.intValue() == 5) {
            l.remove(i);
        }
    }
    
    System.out.println(l);
}

The best way (recommended) is use of java.util.concurrent package. By
using this package you can easily avoid this exception. Refer
Modified Code:

public static void main(String[] args) {
    Collection<Integer> l = new CopyOnWriteArrayList<Integer>();
    
    for (int i=0; i < 10; ++i) {
        l.add(new Integer(4));
        l.add(new Integer(5));
        l.add(new Integer(6));
    }
    
    for (Integer i : l) {
        if (i.intValue() == 5) {
            l.remove(i);
        }
    }
    
    System.out.println(l);
}
寻找一个思念的角度 2025-02-20 13:31:47

迭代器 在另一个线程也修改该集合时并不总是有帮助的。我曾尝试过多种方法,但随后意识到手动穿越该系列要安全得多(向后移动):

for (i in myList.size-1 downTo 0) {
    myList.getOrNull(i)?.also {
       if (it == 5)
          myList.remove(it)
    }
}

Iterators are not always helpful when another thread also modifies the collection. I had tried many ways but then realized traversing the collection manually is much safer (backward for removal):

for (i in myList.size-1 downTo 0) {
    myList.getOrNull(i)?.also {
       if (it == 5)
          myList.remove(it)
    }
}
安穩 2025-02-20 13:31:47

迭代器在某些情况下也可能引起 contrentModificationException

如果单线线程一次访问列表,这将起作用。

    val items = arrayListOf<String>()
    val itemsToBeRemoved = arrayListOf<String>()
    items.add("1")
    items.add("2")
    items.add("3")

    items.forEach {
        if(someCondition){
            // item you want to be removed
            itemsToBeRemoved.add(it)
        }
    }

    itemsToBeRemoved.forEach {
        // remove item
        items.remove(it)
    }
    itemsToBeRemoved.clear()

您可以使用 copyOnwritearRaylist 如果您的列表从 mutliple threads访问。但是请记住,如果您的列表经常更改,它将导致性能问题。 copyOnwritearRaylist 如果您的列表不经常更新,则表现更好。

Iterator is not foolproof and can also cause ConcurrentModificationException in some cases.

This will work if single thread is accessing list at a time.

    val items = arrayListOf<String>()
    val itemsToBeRemoved = arrayListOf<String>()
    items.add("1")
    items.add("2")
    items.add("3")

    items.forEach {
        if(someCondition){
            // item you want to be removed
            itemsToBeRemoved.add(it)
        }
    }

    itemsToBeRemoved.forEach {
        // remove item
        items.remove(it)
    }
    itemsToBeRemoved.clear()

You can use CopyOnWriteArrayList if your list is access from mutliple threads. But remember it will cause performance issues if your list is changing frequently. CopyOnWriteArrayList will perform better if your list is not updating frequently.

故人的歌 2025-02-20 13:31:47

如果 arrayList:remove(int index) - 如果(索引是最后一个元素的位置),则避免没有 system.ArrayCopy(),并且没有时间为此。

如果(索引减少),列表元素的元素也会减少,则阵列时间会增加!

最好的有效删除方法是按降序删除其元素:
while(list.size()&gt; 0)list.remove(list.size() - 1); // take o(1)
while(list.size()&gt; 0)list.Remove(0); // take o(fortorial(n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • 索引循环:1090毫秒
  • desc索引: 519 < /strong>毫秒---最适合
  • 迭代器:1043毫秒

In case ArrayList:remove(int index)- if(index is last element's position) it avoids without System.arraycopy() and takes not time for this.

arraycopy time increases if(index decreases), by the way elements of list also decreases!

the best effective remove way is- removing its elements in descending order:
while(list.size()>0)list.remove(list.size()-1);//takes O(1)
while(list.size()>0)list.remove(0);//takes O(factorial(n))

//region prepare data
ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<Integer> toRemove = new ArrayList<Integer>();
Random rdm = new Random();
long millis;
for (int i = 0; i < 100000; i++) {
    Integer integer = rdm.nextInt();
    ints.add(integer);
}
ArrayList<Integer> intsForIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsDescIndex = new ArrayList<Integer>(ints);
ArrayList<Integer> intsIterator = new ArrayList<Integer>(ints);
//endregion

// region for index
millis = System.currentTimeMillis();
for (int i = 0; i < intsForIndex.size(); i++) 
   if (intsForIndex.get(i) % 2 == 0) intsForIndex.remove(i--);
System.out.println(System.currentTimeMillis() - millis);
// endregion

// region for index desc
millis = System.currentTimeMillis();
for (int i = intsDescIndex.size() - 1; i >= 0; i--) 
   if (intsDescIndex.get(i) % 2 == 0) intsDescIndex.remove(i);
System.out.println(System.currentTimeMillis() - millis);
//endregion

// region iterator
millis = System.currentTimeMillis();
for (Iterator<Integer> iterator = intsIterator.iterator(); iterator.hasNext(); )
    if (iterator.next() % 2 == 0) iterator.remove();
System.out.println(System.currentTimeMillis() - millis);
//endregion
  • for index loop: 1090 msec
  • for desc index: 519 msec---the best
  • for iterator: 1043 msec
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文