List接口常用实现类
ArrayList的实现:
话不多说,直接看源码:
这句话是这样描述的,它是List的实现类,并且是一个可变的数组。实现了List所有的操作,允许存放所有元素,包括空。除了实现List接口外,这个类还提供了操作数组大小的方法,意思集合的长度是可以改变的。最后还提到了这个类和Vector类似,只是不是同步的。很多人都说ArrayList会给一个初始大小,我们通过源码来看看这样的说法到底准确不,当我们创建一个ArrayList的时候,我们看看做了什么:
这里将elementData赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,继续看看它们分别是什么。
看上面的注释,重点是后面两句话当添加第一个元素时,任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空ArrayList将扩展为DEFAULT_CAPACITY。
从注释的描述来看,我们上面说的会有一个初始化的大小貌似没有错,并且大小为10,但是注释里面描述的是将要扩展为10,并没有说在初始化的时候设置为10,我们看构造器里面,只是将其设置为了一个空的数组,所以并不是在初始化的时候给它设置的初始长度。那么到底什么时候设置的呢,我们继续来看,当创建好一个ArrayList对象之后,我们肯定要添加元素到该对象中,所以我们来看add方法:
进入ensureCapacityInternal方法,这里传了一个参数,size初始值为0,所以相当于传了一个1进去:
再来看calculateCapacity方法,这个方法传了两个参数,一个是创建对象的时候设置的一个ArrayList的一个缓存集合,它现在是一个空数组,还有一个就是上面传进来的1:
这个方法逻辑很简单,如果elementData为空集合,即说明是第一次进行添加操作,我们就比较传进来的长度和默认的10,返回大的,所以这里我们也明白了传进来的int数字其实就是自定义的集合长度值,只是这里我们没有选择自定义,所以默认是1.。如果不是第一次的话,则直接返回传进来的长度。再来看ensureExplicitCapacity方法
最终调用了grow方法,来看这个方法的实现:
具体细节逻辑这里就不详细描述了,主要注意圈起来的两个地方,可以得到ArrayList的扩容是每次扩容1.5倍,而具体扩容的原理是通过数组的复制实现的。
根据上面的源码分析,我们可以得到如下结论:
- ArrayList的底层是通过数组实现的。
- ArrayList如果在初始化的时候没有指定长度,那么在使用的时候即调用add方法的时候会设置一个默认的长度10。
- ArrayList装满的时候,程序会自动扩容,扩容至之前的1.5倍。
- ArrayList和Vector十分类似,区别就在于ArrayList是线程不安全的,而Vector是线程安全的。
LinkedList的实现:
老规矩,直接看类的注释:
从上面的描述可以得到LinkedList底层是通过双向链表实现的,可以保存空值,同样它不是线程安全的。
LinkedList的源码非常简单,这里就不介绍了。
Vector的实现:
Vector实现和ArrayList基本上是一致的,唯一的区别就是它是线程安全的,所以效率相较于ArrayList低。
总结:
ArrayList:实现了长度可变的数组,在内存中分配连续的空间。
优点:遍历元素和随机访问元素的效率较高。
缺点:添加和删除需要大量移动元素,效率低,按照内容查找效率低。
LinkedList:采用双向链表存储方式。
优点:插入、删除元素效率较高。
缺点:遍历和随机访问元素效率较低。