相信大家肯定都看过阿里巴巴开发手册,而在阿里巴巴开发手册中明确的指出,不要再foreach循环里面进行元素的add和remove,如果你非要进行remove元素,那么请使用Iterator方式,如果存在并发,那么你一定要选择加锁。
典型错误实例
List list = new ArrayList<>();
list.add("AA");
list.add("DD");
for (String str :list) {
if ("AA".equals(str)){
list.remove(str);
}
}
System.out.println(list);
相信大家执行这个代码的时候没有什么感觉,因为如果你把第一个放进去,执行的时候完全没有任何的问题,大家看结果。
[DD]
Process finished with exit code 0
这是不是和大家想象的内容一模一样,但是大家知道如果我们把 “DD”进行remove的话,会出现什么呢?
List list = new ArrayList<>();
list.add("AA");
list.add("DD");
for (String str :list) {
if ("DD".equals(str)){
list.remove(str);
}
}
System.out.println(list);
先给大家看结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.chuyikeji.jsoup.controller.TestClass.main(TestClass.java:25)
Process finished with exit code 1
对,你没有看错,错了,竟然出错了,这时候为什么呢?
为什么会出现异常
我们直接从异常信息入手,
java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
异常信息中的909行也就是从这里开始的,大家肯定会想他在比较两个值
modCount
和
expectedModCount
,那么这两个变量是什么呢?
其实说白了,他们就是来表示修改次数的变量,其中modCount表示集合的修改次数,这其中包括了调用集合本身的add方法等修改方法时进行的修改和调用集合迭代器的修改方法进行的修改。而expectedModCount则是表示迭代器对集合进行修改的次数。
这话一说,心里是不是直接一个大写的 WC?迭代器,竟然还是迭代器?在这里阿粉就不再详细的去给大家说这两个变量是个什么东西了,大家有兴趣的可以去查看源码一下
AbstractList
的601行之前的注释,
而 expectedModCount 是个什么鬼?从ArrayList 源码可知,这个变量是一个局部变量,也就是说每个方法内部都有expectedModCount 和 modCount 的判断机制,进一步来讲,这个变量就是 预期的修改次数,而这个判断机制,很多人都会直接告诉你说fail-fas机制,你说这个机制,但是很多人都知道,你想解释明白他,需要点功夫的,理解起来肯定更需要花点时间的。
我们在这里也搞搞事,直接给他反编译一下我们的这个 Class,看看是个什么东西弄得我们这么头疼。