0

如果有以下问题:我有一个列表,我正在使用增强的 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)

4

7 回答 7

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

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

于 2012-06-16T11:18:08.777 回答
1

使用Iterator.remove()应该防止异常被抛出。

于 2012-06-16T11:12:32.183 回答
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(),添加 getter/setter 等。java 中的约定是使用 camelCase 命名变量和方法(而不是用“_”分隔它们)。

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

于 2012-06-16T11:14:00.820 回答
0

一般来说,对于子类没有通用的解决方案Collection——如果集合被修改,大多数迭代器将变得无效,除非修改通过迭代器本身通过Iterator.remove().

在实现方面有一个潜在的解决方案ListList接口具有基于索引的添加/获取/设置/删除操作。Iterator您可以使用基于计数器的循环显式地遍历列表,而不是使用实例,就像使用数组一样。但是,您应该注意在插入或删除元素时适当地更新循环计数器。

于 2012-06-16T11:10:34.553 回答
0

来自 ConcurrentModificationException 的 javadoc:

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

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

你正在执行 s.classes.remove(index)

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

于 2012-06-16T11:13:45.560 回答
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();
    }
}
于 2012-06-16T11:14:39.387 回答
0

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

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

If concurrency is the case, then I would recommend using CopyOnWriteArrayList. It doesn't need synchronisation and its for-each iterator doesn't throw ConcurrentModificationException.

于 2012-06-16T12:49:31.013 回答