废话少说,直接上测试代码:
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种方式:已经知道原因和原理了,其实处理方式就很灵活了,可以自己想想其他方式了,哈哈!!!
热门工具 换一换