You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Slick2d无法更新并抛出ConcurrentModificationException问题求助

解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:55:35