【问题标题】:Arraylist iterator: concurrentmodificationArraylist迭代器:并发修改
【发布时间】:2014-02-17 23:52:14
【问题描述】:

occupants 是一个 java.util.ArrayList。

我正在像这样迭代它

public void hitOccupants(SnakeController snakeController){

    ListIterator<Hitable> i = occupants.listIterator();
    while( i.hasNext()){
        Hitable hitable = i.next();
        if(hitable.hit(snakeController)){//returns true if it should be deleted
            i.remove();
        }
    }
}

我可以弄清楚为什么这会产生 ConcurrenModificationException... 我正在使用 libgdx,因此可能存在线程问题。这是我的第一个 libgdx 项目,所以我不确定。在安卓上运行代码。堆栈跟踪:

java.util.ConcurrentModificationException
        at java.util.AbstractList$SimpleListIterator.remove(AbstractList.java:71)
        at com.ninovanhooff.snake.model.BoardSpace.hitOccupants(BoardSpace.java:65)
        at com.ninovanhooff.snake.controller.SnakeController.act(SnakeController.java:77)
        at com.ninovanhooff.snake.controller.BoardController.act(BoardController.java:72)
        at com.ninovanhooff.snake.GameActor$2.act(GameActor.java:77)
        at com.badlogic.gdx.scenes.scene2d.Actor.act(Actor.java:86)
        at com.badlogic.gdx.scenes.scene2d.Group.act(Group.java:48)
        at com.badlogic.gdx.scenes.scene2d.Group.act(Group.java:48)
        at com.badlogic.gdx.scenes.scene2d.Stage.act(Stage.java:225)
        at com.ninovanhooff.snake.SnakeGame.render(SnakeGame.java:66)
        at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:510)
        at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1516)
        at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240)

【问题讨论】:

  • Hitable.hit 会修改列表吗?
  • 是的,在您迭代列表时,还有其他东西正在修改列表。您在自己的迭代中正确使用了迭代器的 remove() 方法,所以这不是问题。
  • 是的,hit 方法有什么作用?粘贴代码。
  • 这段代码每次运行时都会出现这个异常吗?还是只是偶尔?发生这种情况时,列表有什么特别之处吗(单元素列表等)?
  • 投票结束“信息不足”。发布的代码很好:使用i.remove() 在迭代列表时修改列表。 @Nino:如果您可以发布更多关于occupants 是什么以及它的定义位置以及程序如何工作的信息,我们或许可以提供帮助。如果多个线程正在调用它,它可能就像声明这个方法synchronized 一样简单。

标签: java android iterator libgdx


【解决方案1】:

根据 java 文档的 ConcurrentModificationException

检测到并发的方法可能会抛出此异常 在不允许修改时修改对象。

例如一般不允许一个线程修改 一个集合,而另一个线程正在对其进行迭代。一般来说, 在这些情况下,迭代的结果是不确定的。 一些迭代器的实现(包括所有通用的 JRE 提供的目的集合实现)可以选择 如果检测到此行为,则抛出此异常。做的迭代器 这被称为快速失败迭代器,因为它们快速失败并且 干净利落,而不是冒着随意的、非确定性的行为的风险 未来的不确定时间。

简而言之,在迭代列表时不能修改列表。

有多种方法:

1) 创建一个新列表

2) 使用不同的集合,例如 Map 或 Set。

3) 改变命中对象的一些状态

【讨论】:

  • 只要你使用的是迭代器的 remove() 方法,你就可以愉快地修改列表,同时迭代它;这就是它的用途。他的问题实际上是其他东西同时修改列表。
  • @BrianRoach 所以我想念其他答案,还是它们都不正确?我不经常使用 ArrayList(实际上我正在使用 libgdx,所以我使用他们的 Array)
  • @BrianRoach 啊,现在我明白了:P 如果您使用迭代器 remove() 没关系,因为它是为此而设计的。我需要在发布之前考虑一下...我删除了我的误导性评论
  • 每一个说使用迭代器的remove() 方法存在问题的答案都是不正确的,是的。 OP 的问题是其他东西,无论是在他在循环内调用的东西还是在另一个线程中,正在修改列表。
  • @Springrbua 同意;事实上,我只是在主 Q 上发表了关于这种影响的评论。
【解决方案2】:

Hitable.hit() 将一个元素添加到数组中,从而导致并发修改。

将其置于上下文中:

我正在制作经典的蛇游戏。当蛇击中 Hitable 苹果时: 1) 苹果必须从 BoardSpace 的占用者中移除 //BoardSpace == tile 这是在此处讨论的代码中完成的 2) 蛇身被拉长到苹果所在的 BoardSpace 中。蛇现在是 BoardSpace 的占用者,因此蛇被添加到占用者中。

这两个都在 Apple.hit() 中完成,因此:并发修改。

解决方法:我使用了一个CopyOnWriteArrayList,按照原来的顺序和内容遍历占用者,并添加了要移除的元素,以便以后移除。

CopyOnWriteArrayList<Hitable> occupantsSnapshot = new CopyOnWriteArrayList<Hitable>(occupants);
    ArrayList<Hitable> removals = new ArrayList<Hitable>();
    Iterator<Hitable> i = occupantsSnapshot.iterator();
    while (i.hasNext()) {
        Hitable hitable = i.next();
        boolean remove = hitable.hit(snakeController);
        if (remove) {//returns true if it should be deleted
            removals.add(hitable);
        }
    }

    for(Hitable hitable: removals){
        occupants.remove(hitable);
        boardController.removeHitable(hitable);
    }

用于教育目的:Bitbucket snapshot

参见 AppleController 和 SnakeBodyParts。

【讨论】:

    【解决方案3】:

    您不能在遍历 ArrayList 时从其中删除项目。这样做的原因有很多。最明显的是,ArrayList 是动态的,因此如果您删除索引 2 处的对象,则索引 3 处的对象现在会向下移动到索引 2。这很重要,因为在迭代时,如果出现以下情况,它很容易导致越界异常您将在不重新调整迭代器的情况下缩短列表的长度。

    有很多方法可以解决这个问题,但有一个固定规则:如果你在迭代,你就不能删除。因此,要么想办法在不进行迭代的情况下执行此操作(while 循环仅在 hitable.hit 为 false 时增加索引),要么将要删除的对象存储在单独的列表中,然后逐个删除这些项目。

    【讨论】:

    • 这是完全不正确的。 OP 正在使用迭代器的 remove() 方法。为什么你认为ListIteratorIteratorremove() 方法?事实上,您必须使用迭代器的 remove() 方法在迭代列表时从列表中删除某些内容,以便它可以处理您描述的问题。
    • 你是对的。我错误地读到原始代码在发出 .next() 之前有一个 .remove(),这(至少在过去)会导致此错误。但是代码的顺序是正确的。
    猜你喜欢
    • 1970-01-01
    • 2014-10-08
    • 2018-10-21
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 2014-09-20
    • 1970-01-01
    相关资源
    最近更新 更多