Slick2d无法更新并抛出ConcurrentModificationException问题求助
嘿,我一眼就揪出问题所在啦!你遇到的ConcurrentModificationException,是因为在遍历集合的同时直接修改了它——具体来说,你用pros.iterator().forEachRemaining()遍历抛射体集合时,调用pros.remove(p)修改了集合结构,这违反了迭代器的遍历规则,所以触发了异常。另外,你的for (int a = 0; a < delta; a++)循环也有问题:delta是两帧之间的毫秒间隔,这么写会让每帧添加delta个抛射体,很快就会让集合“爆炸”,这可不是你想要的背景抛射体效果哦!
问题根源
当通过迭代器遍历集合时,集合内部会维护一个修改计数器。如果在遍历过程中直接调用集合的remove()方法(而非迭代器自身的remove()),计数器就会和迭代器的预期值不一致,从而触发并发修改异常。
解决方案
我给你两种靠谱的修复方式,同时也修正了添加抛射体的逻辑:
方案1:使用迭代器的remove()方法(最直接)
直接用迭代器的循环遍历,当需要移除元素时调用迭代器自身的remove(),这是安全的集合修改方式:
public void update(GameContainer gc, StateBasedGame game, int delta) throws SlickException { // 修正添加抛射体逻辑:用计时器控制生成节奏,而非循环delta次 addProjectileTimer += delta; // 比如每200ms生成一个抛射体,可根据需求调整间隔 if (addProjectileTimer >= 200 && pros.size() < 5) { pros.add(new MenuProjectile(Images.red[0])); addProjectileTimer = 0; } // 使用迭代器安全遍历并移除元素 Iterator<MenuProjectile> iterator = pros.iterator(); while (iterator.hasNext()) { MenuProjectile p = iterator.next(); if (p.getX() > gc.getWidth() || p.getY() > gc.getHeight()) { // 调用迭代器的remove方法,不会触发异常 iterator.remove(); } else { p.update(); } } } // 需要在类中添加计时器变量 private int addProjectileTimer = 0;
方案2:先收集待移除元素,再批量删除
如果你更习惯用forEach语法,可以先把需要移除的抛射体收集到临时列表中,遍历结束后再批量移除:
public void update(GameContainer gc, StateBasedGame game, int delta) throws SlickException { addProjectileTimer += delta; if (addProjectileTimer >= 200 && pros.size() < 5) { pros.add(new MenuProjectile(Images.red[0])); addProjectileTimer = 0; } // 收集需要移除的元素 List<MenuProjectile> toRemove = new ArrayList<>(); pros.forEach(p -> { if (p.getX() > gc.getWidth() || p.getY() > gc.getHeight()) { toRemove.add(p); } else { p.update(); } }); // 批量移除元素 pros.removeAll(toRemove); } private int addProjectileTimer = 0;
为什么原来的添加逻辑有问题?
delta是当前帧与上一帧的时间差(比如60帧每秒的话,delta大概是16左右),你原来的for (int a = 0; a < delta; a++)会让每帧生成16个抛射体,瞬间就会突破pros.size() <5的限制,完全不符合“背景缓慢发射抛射体”的预期。用计时器控制固定间隔生成,才能还原《愤怒的小鸟》启动界面的效果。
这样修改后,不仅能解决并发修改异常,还能让抛射体的生成节奏更合理,完美实现你想要的效果!
内容的提问来源于stack exchange,提问作者Zhao Yun

