Swift数组遍历删除满足条件元素时出现索引错误的正确处理方法
问题本质
你遇到的索引错误核心原因是:for-in循环遍历的区间0..<features.count在循环启动时就已经固定了取值范围,后续数组删除元素导致长度变短后,循环还是会尝试访问超出当前数组长度的索引,自然触发越界崩溃。
可落地处理方案
以下方案适配不同的业务场景:
倒序遍历(首选无额外开销方案)
从数组最后一位往第一位遍历,删除元素只会影响索引大于当前位置的元素,完全不会干扰还没遍历到的更小索引的元素,不需要额外开辟空间,写法也最简单:
struct Feature { let name: String var remains: Int } var features = [Feature(name: "1", remains: 2), Feature(name: "2", remains: 3), Feature(name: "3", remains: 3)] while true { // 倒序遍历实现安全删除 for index in stride(from: features.count - 1, through: 0, by: -1) { features[index].remains -= 1 if features[index].remains == 0 { features.remove(at: index) } } }
批量过滤生成新数组(大规模数组最优方案)
如果数组规模很大(万级以上元素),原地多次删除元素会触发多次连续内存调整(删除中间元素需要把后面所有元素往前挪一位),性能损耗很高。这种场景推荐一次性完成所有元素的有效期更新和过滤,只做一次数组内存重分配,整体效率远高于多次原地删除:
while true { features = features.compactMap { feature in var updated = feature updated.remains -= 1 return updated.remains > 0 ? updated : nil } }
这个方案不需要额外增加一轮循环,compactMap的单次遍历就同时完成了更新剩余有效期、过滤到期元素两个操作,全程时间复杂度为O(n)。
手动控制索引遍历(适合自定义遍历逻辑场景)
如果有特殊逻辑需要正序遍历,可以用while循环手动控制索引偏移,删除元素时索引不递增,未删除时再往后走一位:
while true { var index = 0 while index < features.count { features[index].remains -= 1 if features[index].remains == 0 { features.remove(at: index) } else { index += 1 } } }
选型参考
- 千级以内元素的数组:三种方案性能差异可以忽略,优先选倒序遍历写法最简洁。
- 万级以上元素/删除比例很高的场景:优先选批量过滤方案,性能最优。
内容的提问来源于stack exchange,提问作者user14534957
相关产品推荐
相关产品推荐

