Kotlin报java.util.ConcurrentModificationException异常如何修复
报错原因
java.util.ConcurrentModificationException是集合的快速失败(fail-fast)机制触发的:所有基于迭代器实现的集合遍历(包括Kotlin的forEach语法糖),都会在迭代器内部维护一个集合修改次数的计数。如果遍历过程中直接调用集合自身的add/remove/clear等会改变集合结构的方法,会导致集合的修改计数和迭代器记录的数值不一致,直接抛出异常。
结合你的代码,核心问题是:你正在遍历的experiments集合,和你执行clear()操作的response.appliedExperiments是同一个对象引用,相当于边遍历同一个集合边清空集合,直接触发了校验。
另外你代码里还有个隐藏的索引越界风险:0..Math.min(...)是 Kotlin 的闭区间写法,边界值会被包含,当response.paymentMethods的长度刚好等于计算出的最小值时,访问paymentMethods[i]会触发数组/列表越界。
修复方案
根据业务场景选以下任意一种方案即可:
- 方案1:遍历过程中只做逻辑判断,用变量标记是否需要修改集合,等整个遍历流程结束后再执行集合修改操作,这是最稳妥的实现方式:
var shouldClearAppliedExperiments = false experiments.forEach { experiment -> if (NAME_VARIANT == experiment.variantName) { val maxTraverseIndex = Math.min(result.size - 1, Constants.MAX_METHODS_APPLIED) for (i in 0..maxTraverseIndex) { val currentRules = response.paymentMethods[i].scoring.rules!! if (currentRules.none { it.ruleName == NAME_RULE }) { shouldClearAppliedExperiments = true } } } } // 遍历全部完成后再修改集合 if (shouldClearAppliedExperiments) { response.appliedExperiments.clear() }
- 方案2:如果业务逻辑要求遍历过程中就要修改集合,可以先把待遍历的集合拷贝一份生成快照,遍历快照时修改原集合不会触发校验:
// 把原集合转成不可变副本再遍历 experiments.toList().forEach { if(NAME_VARIANT == it.variantName) { val maxTraverseIndex = Math.min(result.size - 1, Constants.MAX_METHODS_APPLIED) for (i in 0..maxTraverseIndex) { if (response.paymentMethods[i].scoring.rules!!.none { it.ruleName == NAME_RULE}) { response.appliedExperiments.clear() } } } }
内容的提问来源于stack exchange,提问作者Camila
相关产品推荐
相关产品推荐

