关于List中的常用集合框架的具体实现类无非就是ArrayList以及LinkedList。而关于两者的区别也是在面试中较为高频的考点。
区别如下:
- 两者的底层数据结构不同,ArrayList的底层数据结构使用的是数组,而LinkedList的底层数据结构使用的是链表。
- LinkedList还实现了Deque接口,说明LinkedList还可以作为队列使用。需要注意的是。这个队列还是双端队列。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
换言之,只要实现了Deque接口的都可以作为队列使用
public interface Deque<E> extends Queue<E> {
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
3.ArrayList因为是使用数组作为底层数据结构,那么ArrayList的随机访问的效率超级高,时间复杂度为O(1),但是他的添加和删除元素效率不高,只要添加和删除不是在数组的最后一个位置上进行的话,那么在进行添加和删除必定会涉及到数组元素的移动。还有一种特殊情况就是,在进行添加之前会进行数组长度是否足够的判断,如果不够的话,会进行扩容,扩容这里的机制会耗费相当多的资源,这个涉及到了整个数组的移动。以下就是扩容的源码。可以看到ArrayList扩容每次都会使得数组长度增加原来长度的0.5倍。也就是下面源码的 int newCapacity = oldCapacity + (oldCapacity >> 1);。然后在使用Arrays工具类将数组进行移动。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
然而LinkedList作为使用链表作为底层数据结构的集合,他的随机访问的效率远远低于ArrayList。因为LinkedList只能从头或者从尾部开始遍历。直到遍历到一个下标的值为我想要查找的值然后返回这个下标即可。还有值得注意的是,LinkedList同样可以通过下标进行访问,这个访问做了一定 程度上的优化,就是在访问之前会将这个下标与数组长度的一半就行对比,如果这个下标小于数组长度的一半,就从头部开始遍历,反之就从尾部开始遍历。这种优化能够节省几乎一半的时间。关于查找的源码如下所示:
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
而链表作为底层数据结构最大的优点就是删除和添加节点方便不会涉及到元素的移动,我们每次只需要将他的next 和prev进行相应的修改指向新的引用地址即可。而且使用链表作为数据结构的同时十分灵活方便,可以随时对链表进行扩充。