废话少说,直接上测试代码:
public class ForeachTest {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("hello");
list.add("hello1");
list.add("hello2");
// for (int i = 0; i < list.size(); i++) {
for(String s:list){
list.remove("hello");
System.out.println(s);
}
}
}
结果如下:
然后换一种遍历方式:
public class ForeachTest {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("hello");
list.add("hello1");
list.add("hello2");
for (int i = 0; i < list.size(); i++) {
// for(String s:list){
list.remove("hello");
System.out.println(list.get(i));
}
}
}
结果如下:
以上是两种测试的对比现象(结果),那么既然有了结果接下来就要分析原因了。
控制台抛异常首先定位异常抛出的地方,通过查看ArrayList源码可以得知异常触发点为:
可知,异常的原因是因为modCount != expectedModCount导致的。
接下来就是分别查看这两个参数的含义和可以修改他们的操作了。
-
modCount:是AbstractList的一个成员变量:
protected transient int modCount = 0;
但是ArrayList是继承AbstractList的,这里modCount的修饰符是protected,所以是可以被subclass继承的。
继续查看源码,对modCount的英文解释为:The number of times this list has been <i>structurally modified</i>.翻译:记录该list结构被修改的次数。 查看源码中该变量的修改情况,发现就是在add/remove的时候,修改了modeCount的值。
-
expectedModCount:是ArrayList的一个内部类的成员变量
说明:这里我们知道增强for底层的实现还是通过Iterator实现的,所以这里就不难理解为什么出现在这个地方了。 -
在Iterator遍历开始就设置了两个参数相等,那么是什么情况导致了两个参数不等呢?
前面我们知道在对集合进行add和remove的时候会修改modCount的值,而我们整个iterator都没有修改expectedModCount的值,所以在我们使用增强for遍历的时候,如果进行add/remove的操作,就会导致两个参数不等,然后抛出异常。
fail-fast机制:以上我们是从代码层面分析出了,为什么抛出异常,但是我们其实还不是很清楚为什么要这么设计。其实这是Java的fail-fast 机制,即快速失败机制,是java集合(Collection)中的一种错误检测机制。
详细解释如下:
上面我们基本了解了异常原因和原理,接下来的问题就是我们该如何在业务中处理这种情况呢?
- 第一种方式:我们使用一般的for循环即可;
- 第二种方式:我们使用Iterator自带的remove方法进行操作,因为自带的方法是同步了modCount和expecteModCount的;
- 第三种方式:使用Java8中的filter;
- 第四种方式:新建一个集合来装过滤后的数据(这种方式比较消耗内存,不推荐使用);
- 第N种方式:已经知道原因和原理了,其实处理方式就很灵活了,可以自己想想其他方式了,哈哈!!!
本文地址:http://www.45fan.com/dnjc/100437.html