我怎样才能反击ConcurrentModificationException?

时间:2012-06-16 11:01:55

标签: java for-loop concurrentmodification

如果出现以下问题: 我有一个List,我正在使用增强的for循环。每次我想删除列表,我得到一个ConcurrentModificationException。我已经发现了为什么抛出这个异常,但我不知道如何修改我的代码,以便它的工作。这是我的代码:

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....
    }

我也试过这个实现。

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....
    }

不工作......

是否有常用的标准解决方案? (希望......这不明显:D)

7 个答案:

答案 0 :(得分:3)

出现此问题的主要原因是因为for-each循环的语义含义。

当你使用for-each循环时,正在遍历的数据结构不能被修改。

基本上这种形式的任何东西都会抛出这个异常:

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

作为旁注,您不能在集合中添加或替换元素。

有几种方法可以执行此操作。

一种方法是使用迭代器,并在删除对象时调用remove()方法。

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

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

此选项具有以下属性:删除对象的时间与迭代器的remove方法成比例。

下一个选项是存储要删除的对象,然后在遍历列表后删除它们。在迭代期间删除可能会产生不一致的结果的情况下,这可能很有用。

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

这两种方法适用于一般Collection类型,但对于列表,您可以使用标准for循环并将列表从列表末尾移到前面,删除您喜欢的内容。

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

也可以在正常方向行走并进行移除,但您必须注意增量的发生方式;具体来说,您不希望在删除时增加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
    // ...
}

希望这可以很好地概述这些概念并提供帮助!

答案 1 :(得分:1)

使用Iterator.remove()可以防止抛出异常。

答案 2 :(得分:1)

如果我做得对,你正在迭代一组类,如果一个给定的类符合某些条件你正在寻找它的索引并尝试删除它?

为什么不这样做:

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

如果需要,添加同步(但如果我是你,我更喜欢并发集合),最好将“==”更改为equals(),添加getters / setter等。此外,java中的约定是命名变量和使用camelCase的方法(而不是用“_”分隔它们。)

实际上,这是 使用迭代器的情况之一。

答案 3 :(得分:0)

通常没有Collection子类的通用解决方案 - 如果修改了集合,大多数迭代器将变为无效,除非通过Iterator.remove()通过迭代器本身进行修改

对于List实施, 是一个潜在的解决方案:List接口具有基于索引的add / get / { {3}} / set次操作。您可以使用基于计数器的循环显式遍历列表,而不是使用Iterator实例,就像使用数组一样。但是,在插入或删除元素时,应注意适当更新循环计数器。

答案 4 :(得分:0)

来自ConcurrentModificationException上的javadoc:

“如果一个线程在使用失败快速迭代器迭代集合时直接修改了一个集合,迭代器将抛出此异常。”

所以在你的内心     for(Classes c:s.classes)

你正在执行     s.classes.remove(指数)

并且迭代器正在履行其合同所说的内容。在循环外的范围内声明索引,并在循环完成后删除目标。

答案 5 :(得分:0)

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

答案 6 :(得分:0)

你的for-each迭代器是快速失败的,这就是删除操作失败的原因,因为它会在遍历时更改集合。

您使用的是List接口的哪些实现? 在主题上注意到同步,您是否同时使用此代码?

如果是并发,那么我建议使用CopyOnWriteArrayList。它不需要同步,它的for-each迭代器不会抛出ConcurrentModificationException。