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

为何健身追踪App切换月份后触发ConcurrentModificationException?

解决健身追踪App切换月份后的ConcurrentModificationException问题

嘿,我来帮你拆解下这个困扰你的问题——你遇到的java.util.ConcurrentModificationException,结合错误栈和代码细节来看,核心原因大概率和ArrayList的SubList视图与原列表的强关联有关,具体触发点就是这行代码:

if (completedDates.equals("100")) {completedDatesList.addAll(thisWeekDates);}

问题根源

错误栈里明确指向ArrayList$SubList.size()抛出异常,这说明thisWeekDates很可能是某个原始ArrayList通过subList()方法返回的视图,而非独立的集合实例。ArrayList的SubList本质上只是原列表的一个"动态窗口",它并没有自己存储数据——当原列表发生结构变化(比如切换月份时你修改了原日期列表的元素、新增/删除条目),SubList的内部状态就会和原列表脱节,此时调用SubList的任何方法(比如size(),而addAll()会间接调用它)都会触发ConcurrentModificationException。

另外,如果你在遍历集合的同时修改集合(比如ViewModel的监听回调里同时操作相关日期列表),也会触发这个异常,但结合切换月份的场景,SubList的问题可能性更高。

具体修复方案

  1. 将SubList转为独立集合副本
    不要直接使用subList()返回的对象,而是把它转换成一个新的ArrayList,彻底切断和原列表的关联:

    // 先把thisWeekDates转换成独立副本
    List<Date> thisWeekDatesCopy = new ArrayList<>(thisWeekDates);
    if (completedDates.equals("100")) {
        completedDatesList.addAll(thisWeekDatesCopy);
    }
    

    这样即使原列表后续被修改,副本也不会受影响,自然不会触发异常。

  2. 确保集合操作的线程安全
    你提到已经用了CopyOnWriteArrayList,但要确认所有涉及日期列表的操作都遵循线程安全原则:

    • 如果completedDatesList或原日期列表需要在主线程和后台线程同时操作,务必用CopyOnWriteArrayList替代普通ArrayList,它天生支持并发修改,不会抛出此类异常。
    • 避免在for-each遍历集合的过程中直接修改原集合,改用迭代器的remove()方法,或者先遍历集合副本,再基于副本的结果修改原集合。
  3. 调整周数据拆分逻辑
    在ChangeMonth方法拆分周数据时,检查是否直接返回了原列表的SubList,改成返回新的ArrayList实例:

    // 原错误写法(返回SubList):
    // return originalDates.subList(startIndex, endIndex);
    // 修改为返回独立集合:
    return new ArrayList<>(originalDates.subList(startIndex, endIndex));
    

验证步骤

修改完成后,切换月份并尝试新增、修改或删除训练记录,观察是否还会抛出异常。如果问题依然存在,可以排查ViewModel的监听回调中是否存在并发修改集合的情况,必要时在集合操作处添加同步锁(不过优先使用线程安全集合,锁是兜底方案)。

内容的提问来源于stack exchange,提问作者Josh Brett

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 15:02:59