遍历列表时移除子列表:元素计数与范围移除异常问题
解决有序列表中限制元素出现次数并避免ConcurrentModificationException的问题
嘿,我完全懂你遇到的痛点——要处理有序列表里的元素出现次数,只保留最多m次,不能像removeAll那样把所有相同元素都删掉,结果用subList.clear()或者普通迭代器移除的时候还触发了ConcurrentModificationException对吧?
先说说为什么会出现这个异常:当你在遍历列表(比如用foreach循环或者普通的Iterator遍历)的同时,直接修改原列表(比如调用list.remove()),或者操作原列表的子视图(subList)时,列表的修改次数和迭代器维护的版本号不一致,就会抛出这个异常。
下面给你两种靠谱的解决方案,都是针对有序列表(相同元素连续)的场景:
方法一:使用迭代器安全移除
因为列表是有序的,相同元素会连续出现,我们可以用迭代器跟踪当前元素的出现次数,超过m次时用迭代器自带的remove()方法——这个方法是安全的,它会同步迭代器和列表的修改状态:
public static List<Integer> controlOccurrences(List<Integer> sortedArr, int m) { if (sortedArr == null || sortedArr.isEmpty() || m <= 0) { return new ArrayList<>(); } Iterator<Integer> iterator = sortedArr.iterator(); Integer prev = iterator.next(); int count = 1; while (iterator.hasNext()) { Integer current = iterator.next(); if (current.equals(prev)) { count++; // 超过m次时,用迭代器安全移除当前元素 if (count > m) { iterator.remove(); } } else { // 遇到新元素,重置计数和前一个元素 prev = current; count = 1; } } return sortedArr; }
方法二:利用子列表批量清理指定范围
这种方法先找到每个连续相同元素的区间,然后直接用subList清理超出m次的部分,全程用索引操作,避开迭代器的版本冲突:
public static List<Integer> controlOccurrences(List<Integer> sortedArr, int m) { if (sortedArr == null || sortedArr.isEmpty() || m <= 0) { return new ArrayList<>(); } int i = 0; while (i < sortedArr.size()) { Integer current = sortedArr.get(i); int j = i + 1; // 找到当前元素连续出现的最后位置 while (j < sortedArr.size() && sortedArr.get(j).equals(current)) { j++; } // 如果出现次数超过m,清理从i+m到j的所有元素 if (j - i > m) { sortedArr.subList(i + m, j).clear(); // 清理后列表长度变短,直接跳到保留的最后一个元素位置 i += m; } else { // 没超过次数,跳到下一个不同元素的位置 i = j; } } return sortedArr; }
注意事项
- 如果你的原列表是不可修改的(比如通过
Collections.unmodifiableList创建的),上面两种方法都会抛出UnsupportedOperationException,这时候需要先创建一个原列表的副本:List<Integer> mutableList = new ArrayList<>(sortedArr);,再对副本进行操作。 - 两种方法都依赖列表是有序且相同元素连续的前提,这和你代码里的
sortedArr参数对应,完全适配你的场景。
内容的提问来源于stack exchange,提问作者Mooshstv
相关产品推荐
相关产品推荐

