#集合遍历时删除元素
面试官问:"在遍历 ArrayList 时,怎么安全删除元素?"
候选人小邬答:"用 Iterator.remove()。"
面试官追问:"除了 Iterator,还有别的方法吗?"
小邬答不上来。
【面试官心理】 这道题考查的是候选人对 Java 集合遍历机制的掌握程度。能说出多种安全删除方式的候选人,说明有丰富的实战经验。
#一、常见错误 🔴
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// ❌ 错误一:for-each 循环中删除
for (String s : list) {
if ("b".equals(s)) {
list.remove(s); // ❌ ConcurrentModificationException
}
}
// ❌ 错误二:for 循环正序删除
for (int i = 0; i < list.size(); i++) {
if ("b".equals(list.get(i))) {
list.remove(i); // ❌ 可能跳过元素
// 删除后 i++,i 指向下一个元素,可能跳过
}
}#二、正确方法 🔴
#2.1 Iterator.remove()
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("b".equals(it.next())) {
it.remove(); // ✅ 安全删除
}
}
// list = ["a", "c"]#2.2 removeIf()(JDK 9+)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// ✅ JDK 9+ 推荐
list.removeIf(s -> "b".equals(s));
// list = ["a", "c"]#2.3 倒序 for 循环
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// ✅ 倒序遍历,删除后索引不受影响
for (int i = list.size() - 1; i >= 0; i--) {
if ("b".equals(list.get(i))) {
list.remove(i);
}
}#2.4 ConcurrentHashMap 的安全删除 🟡
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
// ✅ ConcurrentHashMap 可以用 Iterator.remove()
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if ("b".equals(entry.getKey())) {
it.remove(); // ✅ 安全
}
}#三、性能对比 🟡
| 方法 | 时间复杂度 | 适用场景 |
|---|---|---|
| Iterator.remove() | O(n) | 通用 |
| removeIf() | O(n) | JDK 9+,最简洁 |
| 倒序 for | O(n) | JDK 8- |
| Stream.filter() | O(n) | 需要保留部分元素 |
// removeIf 底层实现
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}#四、Stream 方式 🟡
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// ✅ 保留符合条件的
List<String> filtered = list.stream()
.filter(s -> !"b".equals(s))
.collect(Collectors.toList());
// 注意:这是创建新列表,不是修改原列表
// 如果需要修改原列表:
list = list.stream()
.filter(s -> !"b".equals(s))
.collect(Collectors.toList());#五、生产选型 🟡
// JDK 9+:removeIf()(最简洁)
list.removeIf(s -> s.startsWith("test"));
// JDK 8-:Iterator.remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().startsWith("test")) {
it.remove();
}
}
// 多线程场景:ConcurrentHashMap.remove()#六、追问升级
面试官:"ConcurrentHashMap 在遍历时能安全删除吗?"
// ✅ 可以,但有限制
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
// 可以用 Iterator.remove() 安全删除
map.keySet().iterator().remove();
// 但不能:
// map.remove("b") // ❌ 在遍历时可能抛 ConcurrentModificationException
// 不应该在遍历时用 map.remove()
// 应该用 iterator.remove()