ArrayList
JDK8中,ArayList的底层是一个数组,类型是Object类型。同时没有synchronized之类的关键字,说明它是线程不安全的。
常量
//默认的容量大小为10
private static final int DEFAULT_CAPACITY = 10;
//EMPTY_ELEMENTDATA 空对象数组(传了参数的时候使用该数组)
private static final Object[] EMPTY_ELEMENTDATA = {};
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA 缺省对象数组(没有传参数的时候使用该数组)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//实际中数组存放的元素的个数,默认时为0个元素
private int size;
//最大的数组大小为Integer.MAX_VALUE - 8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造器
带int参数
- 如果传入的参数是大于0的数字,那么底层数组就会创建一个Object数组;
- 如果传入的数字是一个0(相当于没有传参)那么将 EMPTY_ELEMENTDATA 这个空的数组赋值给底层数组,没有创建大小;
- 否则抛出异常。
不带参数
- 将空的数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给底层数组,仍然没有创建大小。
没有传参或参数为0的构造方法中,什么时候才会真正的创建数组呢?在第一次调用add方法的时候。
部分方法
重点的方法就是add方法。其他的方法我们看看源码就可以了。
add方法
add方法支持两种:第一种直接add,第二种是像数组那样在index下标add。add方法中,首先通过ensureCapacityInternal方法确定是不是要扩容。然后当前的size下标插入元素e后,让size(插入的元素个数+1)。那ensureCapacityInternal方法中发生了什么?
ensureCapacityInternal方法
假设没有传入参数或参数为0,刚分析源码得知没有创建数组,进入该方法。size表示数组的下标,在+1就是数组的大小,传入size+1是合理的。
进入该方法后,先calculateCapacity数组,在ensureExplicitCapacity刚刚的结果。
calculateCapacity方法
在该方法中,计算要初始化的大小。如果数组是个没有传参的数组,返回默认数组大小10和刚传进来的size + 1;否则返回构造的时候定义的数组大小。总体来说,这个方法用来计算数组的容量。
ensureExplicitCapacity方法
ensureExplicitCapacity方法拿到calculateCapacity方法的容量后,进行判断。如果判断的容量比数组的原来容量大,就扩容,进入grow方法。
grow方法
进入该方法后,先将数组的大小定义为旧的容量,新的容量就是旧的容量的1.5倍。然后进行判断。minCapacity是根据当前有多少个元素来计算的,newCapacity是直接根据数组大小来计算的。
如果新的容量比计算的容量小,新的容量就是计算的容量;如果新的容量超过MAX_ARRAY_SIZE(一个很大的数),就进入hugeCapacity方法。否则,直接创建一个newCapacity大小的数组,拷贝到当前的elementData数组中。
综上,我们就可以得知,在jdk8中使用ArrayList,并不是立即就创建数组,而是第一次调用add方法后才创建。
set方法
remove方法
总结
优点:
(1)根据下标遍历元素和访问元素效率较高。
(2)插入和删除元素的效率不高。
(3)根据元素的值查找元素的下标需要遍历整个元素数组,效率不高。
(4)线程不安全。
LinkedList
JDK8中,LinkedList底层是一个双向的链表。除了可以作为链表之外,还能当作栈和队列来使用。
常量
//链表的长度/大小
transient int size = 0;
//指向第一个节点的指针
transient Node<E> first;
//指向最后一个节点的指针
transient Node<E> last;
构造器
LinkedList构造器中不需要传参数,说明不存在容量这类的问题。
有参数的构造方法,先调用无参数的构造方法,在使用addAll将集合添加到链表的尾部。
add方法
默认添加到LinkedList的尾节点。首先先将last节点赋值给l。新建newNode节点,其中l节点作为prev,null作为next。让last节点指向新创建的节点。如果l节点是空,说明是第一次创建,让first节点指向该newNode,否则l节点的下一个就是newNode节点。最后长度++。
总结
(1)经常使用到插入或删除,可以使用LinkedList,但是它的查找效率低;
(2)LinkedList基于双向循环链表实现,线程不安全。