【问题标题】:Looping through LinkedLinkst in two places gives error在两个地方循环通过 LinkedLinkst 会出错
【发布时间】:2018-06-24 05:35:45
【问题描述】:

所以我正在用 Java 制作一个小游戏,在游戏中你有存储在 LinkedList 中的实体。在 tick 方法中,我像这样遍历实体的 LinkedList:

@Override
public void tick(float delta) {
    for (Entity e : entities) {
        e.tick(delta);
    }
    player.tick(delta);
}

渲染方法也一样

@Override
public void render(Graphics g) {
    g.setColor(Color.DARK_GRAY);
    g.fillRect(0, 0, Game.WIDTH, Game.HEIGHT);
    for(Entity e : entities) {
        e.render(g);
    }
    player.render(g);
}

其中一个实体是一个名为 Block 的类。在 Block 类中,有一个函数会在附近有块时返回,在这个函数中,我还遍历了 Entities 的 LinkedList,它是从 Block 中的以下 tick 方法调用的:

    @Override
public void tick(float delta) {
    if (color == Color.WHITE) {
        if (getSurrounding(-32, -32) != null && getSurrounding(-32, -32).getColor() == Color.BLACK) {
            if (getSurrounding(-32, -32).getStrength() < strength) {
                getSurrounding(-32, -32).setStrength(-50);
            }
        }
    }

    if (!inGame.entities.isEmpty() && strength <= 0) {
        inGame.entities.remove(this); // <------ REMOVING AN ELEMENT
    }
}

public Block getSurrounding(int xOffset, int yOffset) {
    for (Entity e : inGame.entities) {
        if (e instanceof Block) {
            if (x + xOffset == e.getX() && y + yOffset == e.getY()) {
                return (Block) e;
            }
        }
    }
    return null;
}

(这个函数是在block的tick方法中调用和使用的)

现在每次我运行游戏时,游戏都会启动并正常运行几毫秒,然后抛出此异常:

Exception in thread "Thread-2" java.util.ConcurrentModificationException
    at java.util.LinkedList$ListItr.checkForComodification(Unknown Source)
    at java.util.LinkedList$ListItr.next(Unknown Source)
    at _47b3n.game.engine.gamestate.gamestates.InGame.tick(InGame.java:36)
    at _47b3n.game.engine.Handler.tick(Handler.java:17)
    at _47b3n.game.engine.Game.tick(Game.java:70)
    at _47b3n.game.engine.Game.run(Game.java:52)
    at java.lang.Thread.run(Unknown Source)

(前两个代码示例在InGame.java文件中,第36行是tick方法中的for循环) 现在如何停止这个错误?

【问题讨论】:

  • 迭代时不要从列表中删除元素。
  • @hellzone 这并不完全正确。如果他使用Iterator,它会起作用
  • @hellzone 看来发帖者没有删除任何内容
  • @hellzone 我只是在迭代后删除块的 tick 方法中的元素等。所以这不是问题吗?
  • 显然是这样,否则你不会得到异常

标签: java linked-list iteration


【解决方案1】:

ConcurrentModificationException 通常与在循环中迭代列表时修改列表有关。

我的假设是您在迭代时删除了一个元素。

为了阻止这种情况发生,我建议将要删除的对象在循环中(并且满足某些条件)保存在变量中,然后在循环完成后将其从列表中删除。

编辑:根据更新的问题,您正在删除 tick 方法中的一个元素。我不会在此处执行此操作,而是返回您要删除的元素,并且仅当您不再迭代要从中删除的列表时才将其删除。

可能的解决方案: 我将创建另一种方法来检查实体的强度并从 tick 方法中调用它(这是为了保存必须返回要删除的对象的 tick 方法),如下所示:

@Override
public void tick(float delta) {
    Entity removeEntity = null;
    for (Entity e : entities) {
        e.tick(delta);
        if(e.checkStrength()){
           removeEntity = e;
           break;
        }
    }

    if(removeEntity != null){
       entities.remove(removeEntity);
    }

    player.tick(delta);
}

在实体类中:

public boolean checkStrength(){
    if (!inGame.entities.isEmpty() && strength <= 0) {
        return true;
    }
    return false;
}

只是让您知道这不是迄今为止最好的解决方案,但应该可以解决您现在遇到的问题。一定要在你有时间的时候清理你的代码。

【讨论】:

  • 或者只使用Iterator
  • @XtremeBaumer 我已经尝试过一个迭代器,但它不起作用,它给出了同样的错误
  • 我现在添加了代码,实际上我从 LinkedList 中删除了一些东西
  • @XtremeBaumer 我不清楚您为什么认为使用 Iterator 会有所作为(我认为自己是一位经验丰富的 Java 程序员)。无论如何,for-each 循环都有效地使用了“幕后”的迭代器。我错过了什么吗?
  • 当您添加可能的解决方案时,它起作用了!非常感谢!
【解决方案2】:

使用迭代器的解决方案:

将第一个代码块替换为以下内容:

public void tick(float delta) {
    for (Iterator<Entity> iterator = entities.iterator(); iterator.hasNext();) {
      boolean remove = e.tick(delta);
      if(remove) {
          iterator.remove();
      }
    }
    player.tick(delta);
}

然后用这个替换另一个tick方法:

public boolean tick(float delta) {
    if (color == Color.WHITE) {
        if (getSurrounding(-32, -32) != null && getSurrounding(-32, -32).getColor() == Color.BLACK) {
            if (getSurrounding(-32, -32).getStrength() < strength) {
                getSurrounding(-32, -32).setStrength(-50);
            }
        }
    }

    if (!inGame.entities.isEmpty() && strength <= 0) {
        return true;
    }
    return false;
}

这应该可以解决您的问题。方法的返回类型必须改变,以便我们有一个指示符,什么时候删除任何元素,什么时候不删除。每次tick返回true,对应的元素都会被移除

【讨论】:

  • Block 扩展了 Entity 类,这意味着我必须更改整个抽象 Entity 类,以便 tick 方法返回一个布尔值,所以我猜这个解决方案很难。
  • 您提供的关于这些类如何工作以及结构如何的信息非常少。如果您提供更多信息,我们也许可以找到更好的解决方案
【解决方案3】:

您正在删除tick() 方法中的实体...而在该方法中,您仍在迭代外部刻度方法中的实体...因为您正在修改e.tick() 中的列表,列表正在发生变化,这会导致下一次迭代失败......用内联方法替换对e.tick()的方法调用更清楚地说明了问题:

@Override
public void tick(float delta) {
    for (Entity e : entities) {
        //e.tick(delta); --inline below

          if (color == Color.WHITE) {
              if (getSurrounding(-32, -32) != null && getSurrounding(-32, -32).getColor() == Color.BLACK) {
                  if (getSurrounding(-32, -32).getStrength() < strength) {
                      getSurrounding(-32, -32).setStrength(-50);
                  }
              }
          }

          if (!inGame.entities.isEmpty() && strength <= 0) {
              inGame.entities.remove(this); // <------ REMOVING AN ELEMENT
          }
      }

}

请注意,您所说的“删除元素”位于最外层循环内,这就是问题的原因。

编辑:其他人建议的建议解决方案,尝试这样的事情(仅显示删除块):

List toRemove = new ArrayList() //somewhere earlier in the code
if (!inGame.entities.isEmpty() && strength <= 0) {
    //inGame.entities.remove(this); // <------ REMOVING AN ELEMENT
    toRemove.add(this);
}
return toRemove;

然后在调用函数中,在你完成所有迭代之后

entities.removeAll(toRemove);

【讨论】:

  • @RubendeGroot 学会正确使用Iterator
  • 已编辑答案以及可能的解决方案。我不确定迭代器是否能解决当前的问题,因为列表在不同的代码部分中被多次迭代,但也许我的想法不正确。
  • 您在哪里找到tick 方法的代码块?我在问题中找不到它
  • 我看的不够仔细,我认为还有另一个迭代构建元素列表以删除布尔返回更有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-29
  • 2019-03-05
相关资源
最近更新 更多