84669인 학습
152542인 학습
20005인 학습
5487인 학습
7821인 학습
359900인 학습
3350인 학습
180660인 학습
48569인 학습
18603인 학습
40936인 학습
1549인 학습
1183인 학습
32909인 학습
选自《阿里巴巴JAVA开发手册》
图1代码执行情况是:解释删除1这个元素不会报错,但是删除2这个元素报错了,这个情况如何解释?
学习是最好的投资!
从报的错误中可以知道错误的来源checkForComodification(),如果要避免错误需要保持modCount != expectedModCount为false。list.remove(Object)会去调用fastRemove(int)方法,这个时候必然会去修改modCount,这个时候就会出现错误。Iterator iterator = list.iterator();这个方法的实现就是返回一个内部类Itr,(迭代的过程都是使用的这个类),但是为什么这个iterator.remove()不会出现错误了,原因在与这个方法的实现是在进行实际的ArrayList.this.remove之前进行的checkForComodfication检查,remove之后又使expectedModCount = modCount,所以不会出现错误。
checkForComodification()
modCount != expectedModCount
false
list.remove(Object)
fastRemove(int)
modCount
Iterator iterator = list.iterator()
Itr
iterator.remove()
ArrayList.this.remove
checkForComodfication
remove
expectedModCount = modCount
Itr.remove的实现
Itr.remove
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
如果有不对的地方请指出 @叉叉哥 @蒲柳隐逸
单线程的情况下,在遍历List时删除元素,必须要用Iterator的remove方法而不能使用List的remove方法,否则会ConcurrentModificationException。试想如果一个老师正在点整个班级所有学生的人数,而学生如果不遵守纪律一会出去一会进来,老师肯定点不下去。
多线程的情况下,参考我的一篇博客:http://xxgblog.com/2016/04/02...
首先,这涉及多线程操作,Iterator是不支持多线程操作的,List类会在内部维护一个modCount的变量,用来记录修改次数举例:ArrayList源码
protected transient int modCount = 0;
每生成一个Iterator,Iterator就会记录该modCount,每次调用next()方法就会将该记录与外部类List的modCount进行对比,发现不相等就会抛出多线程编辑异常。
为什么这么做呢?我的理解是你创建了一个迭代器,该迭代器和要遍历的集合的内容是紧耦合的,意思就是这个迭代器对应的集合内容就是当前的内容,我肯定不会希望在我冒泡排序的时候,还有线程在向我的集合里插入数据对吧?所以Java用了这种简单的处理机制来禁止遍历时修改集合。
至于为什么删除“1”就可以呢,原因在于foreach和迭代器的hasNext()方法,foreach这个语法糖,实际上就是
while(itr.hasNext()){ itr.next() }
所以每次循环都会先执行hasNext(),那么看看ArrayList的hasNext()是怎么写的:
public boolean hasNext() { return cursor != size; }
cursor是用于标记迭代器位置的变量,该变量由0开始,每次调用next执行+1操作,于是:你的代码在执行删除“1”后,size=1,cursor=1,此时hasNext()返回false,结束循环,因此你的迭代器并没有调用next查找第二个元素,也就无从检测modCount了,因此也不会出现多线程修改异常但当你删除“2”时,迭代器调用了两次next,此时size=1,cursor=2,hasNext()返回true,于是迭代器傻乎乎的就又去调用了一次next(),因此也引发了modCount不相等,抛出多线程修改的异常。
当你的集合有三个元素的时候,你就会神奇的发现,删除“1”是会抛出异常的,但删除“2”就没有问题了,究其原因,和上面的程序执行顺序是一致的。
因为你在对元素进行增删的时候集合中的数量就改变了,那么在遍历的时候就有可能会出现问题.比如一个集合有10个元素,那就应该要遍历10次,当你对增加或删除了一个元素,遍历的次数就不对,所以会报错
倒序删除就可以了,反正list尽量不要remove。可以加delete标记
文档中那个黄色的说明很有意思。
这个例子的执行结果会出乎大家的意料,那么试下把“1”换成“2”,会是同样的结果吗?
这个还是要看ArrayList的源码,一看便知。
ArrayList
倒序删除就可以了
ArrayList不是线程安全的,这样相当于你在遍历的时候修改了List。ArrayList在这种情况下是会抛出并发修改异常的。
从报的错误中可以知道错误的来源
checkForComodification()
,如果要避免错误需要保持modCount != expectedModCount
为false
。list.remove(Object)
会去调用fastRemove(int)
方法,这个时候必然会去修改modCount
,这个时候就会出现错误。Iterator iterator = list.iterator()
;这个方法的实现就是返回一个内部类Itr
,(迭代的过程都是使用的这个类),但是为什么这个iterator.remove()
不会出现错误了,原因在与这个方法的实现是在进行实际的ArrayList.this.remove
之前进行的checkForComodfication
检查,remove
之后又使expectedModCount = modCount
,所以不会出现错误。Itr.remove
的实现如果有不对的地方请指出 @叉叉哥 @蒲柳隐逸
单线程的情况下,在遍历List时删除元素,必须要用Iterator的remove方法而不能使用List的remove方法,否则会ConcurrentModificationException。试想如果一个老师正在点整个班级所有学生的人数,而学生如果不遵守纪律一会出去一会进来,老师肯定点不下去。
多线程的情况下,参考我的一篇博客:http://xxgblog.com/2016/04/02...
首先,这涉及多线程操作,Iterator是不支持多线程操作的,List类会在内部维护一个modCount的变量,用来记录修改次数
举例:ArrayList源码
每生成一个Iterator,Iterator就会记录该modCount,每次调用next()方法就会将该记录与外部类List的modCount进行对比,发现不相等就会抛出多线程编辑异常。
为什么这么做呢?我的理解是你创建了一个迭代器,该迭代器和要遍历的集合的内容是紧耦合的,意思就是这个迭代器对应的集合内容就是当前的内容,我肯定不会希望在我冒泡排序的时候,还有线程在向我的集合里插入数据对吧?所以Java用了这种简单的处理机制来禁止遍历时修改集合。
至于为什么删除“1”就可以呢,原因在于foreach和迭代器的hasNext()方法,foreach这个语法糖,实际上就是
所以每次循环都会先执行hasNext(),那么看看ArrayList的hasNext()是怎么写的:
cursor是用于标记迭代器位置的变量,该变量由0开始,每次调用next执行+1操作,于是:
你的代码在执行删除“1”后,size=1,cursor=1,此时hasNext()返回false,结束循环,因此你的迭代器并没有调用next查找第二个元素,也就无从检测modCount了,因此也不会出现多线程修改异常
但当你删除“2”时,迭代器调用了两次next,此时size=1,cursor=2,hasNext()返回true,于是迭代器傻乎乎的就又去调用了一次next(),因此也引发了modCount不相等,抛出多线程修改的异常。
当你的集合有三个元素的时候,你就会神奇的发现,删除“1”是会抛出异常的,但删除“2”就没有问题了,究其原因,和上面的程序执行顺序是一致的。
因为你在对元素进行增删的时候集合中的数量就改变了,那么在遍历的时候就有可能会出现问题.比如一个集合有10个元素,那就应该要遍历10次,当你对增加或删除了一个元素,遍历的次数就不对,所以会报错
倒序删除就可以了,反正list尽量不要remove。可以加delete标记
文档中那个黄色的说明很有意思。
这个还是要看
ArrayList
的源码,一看便知。倒序删除就可以了
ArrayList不是线程安全的,这样相当于你在遍历的时候修改了List。
ArrayList在这种情况下是会抛出并发修改异常的。