学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话欢迎加入Java学习交流群,群号码: 我们一起学Java!
先看下面这段代码:
运行结果:
峩们不忙看checkForComodification()方法的具体实现我们先根据程序的代码一步一步看ArrayList源码的实现:
首先看ArrayList的iterator()方法的具体实现,查看源码发现在ArrayList的源码中并沒有iterator()这个方法那么很显然这个方法应该是其父类或者实现的接口中的方法,我们在其父类AbstractList中找到了iterator()方法的具体实现下面是其实现代码:
从这段代码可以看出返回的是一个指向Itr类型对象的引用,我们接着看Itr的具体实现在AbstractList类中找到了Itr类的具体实现,它是AbstractList的一个成员内蔀类下面这段代码是Itr类的所有实现:
首先我们看一下它的几个成员变量:
cursor:表示下一个要访问的元素的索引,从next()方法的具体实現就可看出
lastRet:表示上一个访问的元素的索引
好了到这里我们再看看上面的程序:
如果下一个访问的元素下标不等于ArrayList的大小,就表示有元素需要访问这个很容易理解,如果下一个访问元素的下标等于ArrayList的大小则肯定到达末尾了。
然后通过Iterator的next()方法获取到下標为0的元素我们看一下next()方法的具体实现:
接着往下看,程序中判断当前元素的值是否为2若为2,则调用list.remove()方法来删除该元素
通過remove方法删除元素最终是调用的fastRemove()方法,在fastRemove()方法中首先对modCount进行加1操作(因为对集合修改了一次),然后接下来就是删除元素的操作最后将size進行减1操作,并将引用置为null以方便垃圾收集器进行回收工作
接着看程序代码,执行完删除操作后继续while循环,调用hasNext方法()判断由于此时cursor为1,而size为0那么返回true,所以继续执行while循环然后继续调用iterator的next()方法:
注意,像使用for-each进行迭代实际上也会出现这种问题
既然知道原因了,那么如何解决呢
其实很简单,细心的朋友可能发现在Itr类中也给出了一个remove()方法:
在这个方法中删除元素实际上调用的就是list.remove()方法,但是它多了一个操作:
因此在迭代器中如果要删除元素的话,需要调用Itr类的remove方法
將上述代码改为下面这样就不会报错了:
上面的解决办法在单线程环境下适用,但是在多线程下适用吗看下面一个例子:
运行结果:
有可能有朋友说ArrayList是非线程安全的容器,换成Vector就没问题了实际上换成Vector还是会出现这种错误。
原因在于虽然Vector的方法采用了synchronized进行了同步,但是由于Vector是继承的AbstarctList因此通过Iterator来访问容器的话,事实上是不需要获取锁就可以访问那么显然,由于使用iterator对容器进行访问不需要获取锁在多线程中就会造成当一个线程删除了元素,由于modCount是AbstarctList的成员变量因此可能会导致在其他线程ΦmodCount和expectedModCount值不等。
就比如上面的代码中很显然iterator是线程私有的,
因此一般有2种解决办法:
关于并发容器的内容将在下一篇文章中講述
学习Java的同学注意了!!!
学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群群号码: 我们一起学Java!