Skip to content

面试速答(先看这里)

**一句话结论:**我们知道,在foreach的同时修改集合,会触发fail-fast机制,要避免fail-fast机制,有如下处理方案:

60秒标准回答:

我们知道,在foreach的同时修改集合,会触发fail-fast机制,要避免fail-fast机制,有如下处理方案

上面的方式需要做i--避免漏删,还有个办法,那就是倒叙遍历也能避免这个问题

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**上面的方式需要做i--避免漏删,还有个办法,那就是倒叙遍历也能避免这个问题:

**记忆锚点:**Stream → fail-fast → removeIf → List → list → for

易错提醒:

  • 我们知道,在foreach的同时修改集合,会触发fail-fast机制,要避免fail-fast机制,有如下处理方案: 通过普通的for循环( 不建议,可能会漏删 ) 通过普通的for循环进行倒叙遍历( 也能用 ) 上面的方式需要做i--避免漏删,还有个办法,那就是倒叙遍历也能避免这个问题: 使用迭代器循环( 可以用 ) 将原来的copy一份副本,遍历原来的…

加分表达:

  • ( 建议,简单高效 ) 通过removeIf方法,实现元素的过滤删除。

追问准备:

  • 围绕「Stream」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「fail-fast」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「removeIf」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

我们知道,在foreach的同时修改集合,会触发fail-fast机制,要避免fail-fast机制,有如下处理方案:

  1. 通过普通的for循环(不建议,可能会漏删)
java
public void listRemove() { 
    List<Student> students = this.getStudents(); 
    for (int i=0; i<students.size(); i++) { 
        if (students.get(i).getId()%3 == 0) { 
            Student student = students.get(i); 
            students.remove(student); 
            //做一次i--,避免漏删
            i--;
        } 
    } 
}
  1. 通过普通的for循环进行倒叙遍历(也能用)

上面的方式需要做i--避免漏删,还有个办法,那就是倒叙遍历也能避免这个问题:

java
public void listRemove() { 
    List<Student> students = this.getStudents(); 
    for (int i = students.size() - 1; i >= 0; i--) { 
        if (students.get(i).getId()%3 == 0) { 
            Student student = students.get(i); 
            students.remove(student); 
        } 
    } 
}
  1. 使用迭代器循环(可以用)
java
public void iteratorRemove() { 
    List<Student> students = this.getStudents(); 
    Iterator<Student> stuIter = students.iterator(); 
    while (stuIter.hasNext()) { 
        Student student = stuIter.next(); 
        if (student.getId() % 2 == 0) {
            //这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException 
        	stuIter.remove();
        } 
    }
}
  1. 将原来的copy一份副本,遍历原来的list,然后删除副本(可以用,fail-safe的,但是比较复杂)
java
public void copyRemove() {
    // 注意,这种方法的equals需要重写
	List<Student> students = this.getStudents();
    List<Student> studentsCopy = deepclone(students);
    for(Student stu : students) {
        if(needDel(stu)) {
            studentsCopy.remove(stu);
        }
    }
}
  1. 使用并发安全的集合类(可以用,但是需要转成线程安全的集合)
java
public void cowRemove() { 
    List<String> students = new CopyOnWriteArrayList<>(this.getStudents());
    for(Student stu : students) {
        if(needDel(stu)) {
            students.remove(stu);
        }
    }
}
  1. 通过Stream的过滤方法,因为Stream每次处理后都会生成一个新的Stream,不存在并发问题,所以Stream的filter也可以修改list集合。(建议,简单高效)
java
public List<String> streamRemove() { 
    List<String> students = this.getStudents();
    return students.stream()
        .filter(this::notNeedDel)
        .collect(Collectors.toList());
}
  1. 通过removeIf方法,实现元素的过滤删除。从Java 8开始,List接口提供了removeIf方法用于删除所有满足特定条件的数组元素(推荐)
java
arraylist.removeIf(this::needDel);