源码版本是java1.8。主要是分析他的增删查改,扩容机制,迭代器使用等。他基本复写了AbstractList
和AbstractCollection
中有所实现的方法。
构造方法
- 无参构造
- 给定一个初始化大小
- 把一个Collection的实现类通过
toArray()
方法转换成当前ArrayList可以装下的列表。1
2
3ArrayList()
ArrayList(int initialCapacity)
ArrayList(Collection<? extends E> c)
数据结构
我们首先需要了解一下两个外部方法
Arrays.copyOf
方法,他有几个重载的同名方法,但是作用都是类似的。他的作用是可以把一个数组赋通过System.arraycopy
复制到一个新的数组当中,可以根据类型,需要复制的目标长度等进行复制,然后返回新数组。System.arraycopy
方法,他的一个主要方法是arraycopy(Object src, int srcPos, Object dest, intdestPos, int length)
,其中src
是原数组,srcPos
是原数组开始复制的坐标,dest
是目标数组,intdestPos
是目标位置的开始坐标,length
是复制的长度。
在ArrayList内部做一些数据操作,比如移除,扩容等的操作的时候基本都会使用到上面的两个方法去做。
ArrayList的存储数据结构是使用Object[]
数组来存储,从这里我们就可以看得出泛型他是一种编译期的行为,因为Object是可以存储任何数据类型的。他不是用的E[]
这样的泛型数组存储,看看他的成员变量:
1 | //这是序列化的id |
扩容机制
因为在java中,数组一旦定义长度就不可以更改,但是我们的ArrayList是相当于可变长度数组,所以我们需要达到这个需求,就需要对elementData
数组进行扩容,以add(E var1)
方法为例。
- 调用该方法是往
elementData
中的末尾追加一个元素 - 调用之前需要通过
ensureCapacityInternal(int var1)
来保证当前elementData
的长度是>=
当前的size+1
- 通过
calculateCapacity(Object[] var0, int var1)
方法来确定当前的长度,假如当前的,elementData
为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,则取当前的DEFAULT_CAPACITY
和size+1
中的最大者,假如不是则取size+1
- 得到长度之后,通过
ensureExplicitCapacity(int var1)
方法来与当前的elementData
数组长度进行比较,假如该长度比elementData.length
大(不是相等),则进行扩容 - 通过
grow(int var1)
方法进行扩容,var1
是最少需要的长度,详细代码和解析如下
1 | private void grow(int var1) { |
方法和注意点
add
相关的方法,包括addAll
,是往里面增加元素get
获取某个位置的元素set
设置某个位置的元素clear
清除全部元素remove
,这个方法需要我们调用的时候注意,假如我们使用的是ArrayList<Integer>
,假如需要移除的是某个元素,则需要进行类型声明,比如list.remove((Integer)1)
,否则是移除对应位置的元素。removeAll
则是移除elementData
中包含在传入参数的元素。比如
1 | List<String> list = new ArrayList<>(); |
retainAll
求与传入集合的交集,这个方法会改变当前集合,他只会保留相同的,不相同的去除。toArray
把集合转为数组,需要注意假如是传入数组参数的话,他的长度加入比当前集合小,则是把集合前半部分(传入数组的长度)然后返回,否则是整个集合复制然后返回contains
,是否包含某个元素isEmpty
是否是空集合size()
集合的当前元素长度indexOf
,定位第一个出现索引到的元素的位置,lastIndexOf
则是相反,是定位到最后一个与传入参数相同的元素的位置。subList
,返回一个从指定开始和结束位置的ArrayList,他不是我们java.util.ArrayList
,而是这个ArrayList
的子类SubList
,当不做修改的时候,也就是subList
的返回值只是比如用来get()
,set
之类的非结构性修改(不改长度)则会与源集合互相影响,当做结构性修改,比如remove
之类的,则只能subList
去做而且会反映到源集合,而源集合不能做结构性修改,否则会报错forEach
,这是java8中的用来自身直接替换一般的循环replaceAll
,对每一个元素进行某种规则的替换sort
,使用某一种排序方法对集合进行排序。Iterator
返回一般的迭代器listIterator
返回ListIterator对象,可以进行增删改
迭代器
主要是讲ListIterator,在ArrayList中实现了该接口,实现的内部类是ArrayList.ListItr
,他的构造方法是ListItr(int var2)
,传入的参数是某个元素的位置,传入之后就把ListIterator的游标指向这里,然后通过内部类可以访问外部类的方法来实现修改elementData
的目的。他可以向前或者向后遍历都是可以的。
其他实现接口
Cloneable
,实现了这个接口则是说明ArrayList可以通过clone()
方法进行复制Serializable
,实现了这个说明可以进行序列化RandomAccess
,这个是一个标记性接口,它的作用就是用来快速随机存取,实现了该接口,那么使用普通的for循环来遍历,性能更高