1、ArrayList介绍
ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。
1)ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
2)ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。
3)ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
4)ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
结构如下图:
2、ArrayList的初始容量是10,扩容时原来的1.5倍,ArrayList中方法都比较简单,这里不做讲述了,这里,下面注重将一下使用iterator遍历的时候为什么会出现ConcurrentModificationException异常
1、foreach循环遍历,list直接remove
debug源码可以发现,foreach循环遍历会在编译期会被优化为iterator迭代器遍历,源码如下:
2、iterator迭代器遍历,list直接remove
结论:上面两种方式都会抛出ConcurrentModificationException异常,这是因为刚初始的时候,expectedModCount(iterator变量) 和 modCount(list变量) 值相等,后来经过remove后,modCount 值加1了,而iterator中的expectedModCount变量没有同步更新,还是原来的值,调用checkForComodification()方法后就会抛出这个异常
3、for索引循环遍历,其结果预期为空的,但是结果是2,和我们预期的不一样,如下图:
产生此问题的原因是,for索引循环时,list不停的在删除对应索引位置的对象,但是每一次删除后,list的size都在变化(这里为什么只执行了2此循环,就是因为经过remove后list的size变为了不符合循环条件比较参数了),变化后再按照原来的索引删除就会出现错误情况,如下所示:
换种思路我们可以解决上述问题,下面提供两种方案:
1) 一直遍历list,每次删除都是第一个元素,可以达到删除list中所有元素的目的:
2) 倒着遍历删除元素
4、使用iterator迭代器遍历,并使用iterator迭代器remove,得到了我们想要的结果
debug调试源码如下:
结论:从以上分析得知,虽然每次remove后list的size都会变小,但是游标会回退到初始值,下一个元素索引也会设置为删除的当前元素索引,故会出现正确的结果
最后建议:
从以上分析得出,如果要删除list中的元素,最好使用iterator迭代器方式删除,这样子不仅可以删除全部元素,最重要的是可以删除指定的对象,而每次删除都是第一个元素和倒着循环删除不能做到删除指定的对象