简体   繁体   English

我如何应对ConcurrentModificationException?

[英]How can i counter a ConcurrentModificationException?

if have the following problem: I have a List which i am going through using the enhanced for loop. 如果有以下问题:我有一个列表,我正在使用增强的for循环进行。 Every time i want to remove sth, out of the list, i get a ConcurrentModificationException. 每次我想从列表中删除某物时,都会收到ConcurrentModificationException。 I already found out why this exception is thrown, but i don`t know how i can modify my code, so that its working. 我已经找到了引发此异常的原因,但是我不知道如何修改我的代码以使其正常工作。 This is my code: 这是我的代码:

for(Subject s : SerData.schedule)
    {
        //Checking of the class is already existing
        for(Classes c : s.classes)
        {
            if(c.day == day &c.which_class == which_class)
            {
                int index = getclassesindex(s.classes, new Classes(day, which_class));
                synchronized (s) {
                    s.classes.remove(index);

                }
            }
        }
            //More code....
    }

I also tried out this implementation. 我还尝试了此实现。

for(Subject s : SerData.schedule)
    {
        //Checking of the class is already existing
        Iterator<Classes> x = s.classes.iterator();
        while(x.hasNext())
        {
            Classes c = x.next();
            if(c.day == day &c.which_class == which_class)
            {
                int index = getclassesindex(s.classes, new Classes(day, which_class));
                synchronized (s) {
                    s.classes.remove(index);
                }
            }
        }
        //More code....
    }

not working either... 也不起作用...

Is there a common used, standard solution? 有没有常用的标准解决方案? (Hopefully sth. that is not obvious :D ) (希望……不是很明显:D)

The main reason this issue occurs is because of the semantic meaning of your for-each loop. 发生此问题的主要原因是因为for-each循环的语义。

When you use for-each loops, the data structure that is being traversed cannot be modified. 使用for-each循环时, 无法修改正在遍历的数据结构。

Essentially anything of this form will throw this exception: 本质上,这种形式的任何东西都会抛出此异常:

for( Object o : objCollection )
{
    // ...
    if ( satisfiesSomeProperty ( o ) )
       objList.remove(o);    // This is an error!!
    // ...
}

As a side note, you can't add or replace elements in the collection either. 附带说明,您也不能添加或替换集合中的元素。

There are a few ways to perform this operation. 有几种方法可以执行此操作。

One way is to use an iterator and call the remove() method when the object is to be removed. 一种方法是使用迭代器并在要remove()对象时调用remove()方法。

Iterator <Object> objItr = objCollection.iterator();

while(objItr.hasNext())
{
    Object o = objItr.next();
    // ...
    if ( satifiesSomeProperty ( o ) )
        objItr.remove();    // This is okay
    // ...
}

This option has the property that removal of the object is done in time proportional to the iterator's remove method. 此选项的属性是,对象的remove与迭代器的remove方法成比例地及时完成。

The next option is to store the objects you want to remove, and then remove them after traversing the list. 下一个选项是存储要删除的对象,然后在遍历列表后将其删除。 This may be useful in situations where removal during iteration may produce inconsistent results. 这在迭代过程中删除可能会产生不一致结果的情况下很有用。

Collection <Object> objsToRemove = // ...
for( Object o : objCollection )
{
    // ...
    if ( satisfiesSomeProperty ( o ) )
       objsToRemove.add (o);
    // ...
}
objCollection.removeAll ( objsToRemove );

These two methods work for general Collection types, but for lists, you could use a standard for loop and walk the list from the end of the list to the front, removing what you please. 这两种方法适用于常规的Collection类型,但是对于列表,您可以使用标准的for循环并将列表从列表的末尾移到最前面,从而删除您所需要的内容。

for (int i = objList.size() - 1; i >= 0; i--)
{
    Object o = objList.get(i);
    // ...
    if ( satisfiesSomeProperty(o) )
       objList.remove(i);
    // ...
}

Walking in the normal direction and removing could also be done, but you would have to take care of how incrementation occurs; 也可以沿正常方向行走并移除,但您必须注意增量的发生方式。 specifically, you don't want to increment i when you remove, since the next element is shifted down to the same index. 具体来说,您不希望在删除时增加i ,因为下一个元素将下移到相同的索引。

for (int i = 0; i < objList.size(); i++)
{
    Object o = objList.get(i);
    // ...
    if ( satisfiesSomeProperty(o) )
    {
       objList.remove(i);
       i--;
    }

    //caveat: only works if you don't use `i` later here
    // ...
}

Hope this provides a good overview of the concepts and helps! 希望对本文的概念有一个很好的概述并有所帮助!

使用Iterator.remove()应该可以防止引发异常。

Hm if I get it right you are iterating over a collection of classes and if a given class matches some criteria you are looking for the its index and try to remove it? 嗯,如果我做对了,您正在遍历一个类的集合,如果给定的类符合某些条件,则您正在寻找其索引并尝试将其删除?

Why not just do: 为什么不做:

Iterator<Classes> x = s.classes.iterator();
while(x.hasNext()){
    Classes c = x.next();
    if(c.day == day && c.which_class == which_class) {
        x.remove();
    }
}

Add synchronization if need be (but I would prefer a concurrent collection if I were you), preferably change the "==" to equals(), add getters/setters etc. Also the convention in java is to name variables and methods using camelCase (and not separating them with "_"). 添加同步(如果需要)(但如果需要的话,我更喜欢并发集合),最好将“ ==”更改为equals(),添加getters / setters等。java中的约定也是使用camelCase命名变量和方法(并且不要用“ _”分隔)。

Actually this is one of the cases when you have to use an iterator. 实际上,这是必须使用迭代器的情况之一。

There is no general solution for Collection subclasses in general - most iterators will become invalid if the collection is modified, unless the modification happens through the iterator itself via Iterator.remove() . 通常,对于Collection子类没有通用的解决方案-如果修改了collection,则大多数迭代器都将变为无效, 除非修改是通过Iterator.remove()通过Iterator.remove()器本身进行的。

There is a potential solution when it comes to List implementations: the List interface has index-based add / get / set / remove operations. 一个潜在的解决方案,当涉及到List的实现:在List接口具有指数型 / 获取 / 设置 / 删除操作。 Rather than use an Iterator instance, you can iterate through the list explicitly with a counter-based loop, much like with arrays. 您可以使用基于计数器的循环来显式地遍历列表,而不是使用Iterator实例,就像使用数组一样。 You should take care, however, to update the loop counter appropriately when inserting or deleting elements. 但是,在插入或删除元素时,应注意适当地更新循环计数器。

From the javadoc on ConcurrentModificationException: 从javadoc上的ConcurrentModificationException:

"if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception." “如果线程在使用快速失败迭代器迭代集合时直接修改了集合,则迭代器将抛出此异常。”

So within your for (Classes c : s.classes) 因此,在您的for中(类c:s.classes)

you are executing s.classes.remove(index) 您正在执行s.classes.remove(index)

and the iterator is doing just what its contract says. 而且迭代器正在执行其合同规定的内容。 Declare the index(es) in a scope outside the loop and remove your target after the loop is done. 在循环之外的范围中声明索引,并在循环完成后删除目标。

Iterator<Classes> classesIterator = s.classes.iterator();
while (classesIterator.hasNext()) {
    Classes c = classesIterator.next();
    if (c.day == day && c.which_class == which_class) {
        classesIterator.remove();
    }
}

Your for-each iterator is fail-fast and this is why remove operation fails as it would change the collection while traversing it. 您的for-each迭代器是快速失败的,这就是为什么remove操作失败的原因,因为它在遍历集合时会更改集合。

What implementation of List interface are you using? 您正在使用List接口的哪种实现? Noticed synchronisation on Subject, are you using this code concurrently? 注意到在Subject上的同步,您正在同时使用此代码吗?

If concurrency is the case, then I would recommend using CopyOnWriteArrayList . 如果是并发,那么我建议使用CopyOnWriteArrayList It doesn't need synchronisation and its for-each iterator doesn't throw ConcurrentModificationException. 它不需要同步,它的for-each迭代器也不会抛出ConcurrentModificationException。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM