ArrayList
ArrayList是我们在开发中最常用的数据存储容器,它的底层是通过数组来实现的。我们在集合里面可以存储任何类型的数据, 而且他是一个顺序容器,存放的数据顺序就是和我们放入的顺序是一致的,而且他还允许我们放入null元素,我们可以画个图理解一下。
这个图可能不是很正确,里面存放的元素的引用,所以我用了个000x,大致了解一下就行,一个伪图。
这样的话我们来看看源码分析
源码分析
DEFAULT_CAPACITY 这是默认的初始容量,容量是10. EMPTY_ELEMENTDATA 这代表的是一个空的数组,初始化数组。 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 这个是区别上边的那个自定义容量为0的时候的空数组。
有些看源码的就会发现为什么初始容量为10,有会出现一堆什么空数组容量为0的呢? 这就得接下来看一下他的构造了
看这里
构造
注释的意思是构造一个初始容量为10的数组,但是构造函数只是给elementDate赋值了一个空数组,其实就是在我们添加元素的时候,容量自动扩充为10.
我们在看看构造具有指定初始容量的空列表。
从以上的源码我们能够看出来,如果是使用无参构造时,是把DEFAULTCAPACITY_EMPTY_ELEMENTDATA 给了elementDate ,当initialCapacity为0的时候,就把EMPTY_ELEMENTDATA赋值给了elementDate,如果initialCapacity大于0,就会初始化一个initialCapacity长度的数组给elementDate。
这上边的就是我们如果给定初始容量的时候他会在底层干的事情
至于使用方法,add,get这些方法就不仔细的去说了,都能看懂。我们主要来说他的迭代器 也就是inertor。
使用过ArrayList的人一般都知道,在执行for循环的时候一般情况是不会去执行remove的操作的,因为remove的操作会改变这个集合的大小, 所以会有可能出现数组角标越界异常,我们可以试一下。 看图
下面则是他出现异常的代码
foreach循环在我们的印象中不就是inertor么?但是他就是会出现异常,所以我们得继续看源码介绍
在这个方法内部next是最主要的一个方法,他首先去判断了expectedModCount和modCount是否一样,然后去看cursor,是不是超过 集合的大小和数组的长度,然后去吧cursor的值给lastRet,返回的是下标lastRet的元素,最后cursor加1,这样就是说没调用一次next方法, cursor和lastRet都会加1。
当我们在调用remove方法的时候,他会去判断lastRet是否小于0,然后去判断expectedModCount和modCount是否一样,然后他去调用ArrayList.remove()方法 去删除下标是lastRet的元素,然后把lastRet赋值给cursor,然后初始化lastRet = -1 ,最后把modCount重新赋值给expectedModCount。
这个关键的地方来了,remove方法对modCount进行了修改,这个时候expectedModCount和modCount是不一致的,这时候就会出现图中出现的那个异常了。 ConcurrentModificationException异常,而这个异常就是出自ArrayList中的内部类Itr中的checkForComodification方法。
不光是remove这个方法会出现这个,如果你使用add方法的时候也是会出现这个异常的,原理都是一样的都是因为modCount和expectedModCount不相等导致的原因。
ArrayList的结构看完了我们在来看看同样是List的实现类中的LinkedList把
LinkedList
首先啊,这个LinkedList它和ArrayList这数据结构是完全不一样的,ArrayList底层我们已经看过了是数组的结构,而LinkedList的底层则是链表的结构, 它可以进行高效的插入和移除的操作,他基于的是一个双向链表的结构,我们画个图理解一下。
LinkedList的Node节点结构