LinkedList及其和ArrayList区别
文章目录
一、LinkedList概述
LinkedList实现了List接口和Queue接口,继承了AbstractCollection类,它可以用作队列,也可以用作栈。
1.1 LinkedList基本结构
LinkedList有size属性,指明当前拥有的节点数,还有两个指针分别指向这个链表的头部和尾部,用于链表的维护。其对应的Node节点有前向指针和后向指针,因此LinkedList本质上是一个双向链表。
- LinkedList的属性
// 链表中的节点数量
transient int size = 0;
// 链表的头结点
transient Node<E> first;
// 链表的尾节点
transient Node<E> last;
- Node节点
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
1.2 LinkedList的基本方法
- add(E e)——默认尾插
// add方法默认采用尾插
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
- node(int index)——节点查找
通过与当前链表大小一半进行比较,判断索引是靠近链表头部,还是靠近链表尾部。如果靠近头部从头遍历,靠近尾部从末尾开始向前遍历。
Node<E> node(int 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;
}
}
- get(int index)
LinkedList不支持随机访问,但是List 接口依然可以通过下标去 get(),其具体实现在于每个节点有前后指针,根据这个找。
public E get(int index) {
// 检查 index 是否越界
checkElementIndex(index);
// 调用上面的 node 方法
return node(index).item;
}
二、LinkedList和ArrayList区别
- LinkedList和ArrayList的差别主要是数据结构的不同。ArrayList是基于数组实现的,而LinkedList是基于双向链表实现的。
- LinkedList实现了Deque接口,Deque接口是Queue接口的子接口,它是一个双端队列,因此LinkedList可以作为队列、栈和List集合使用,功能更加强大。
- ArrayList是基于index的数据结构,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有更好的性能。但是插入、删除等却开销很大,因为需要调用arraycopy()方法移动数组中插入位置之后的所有元素。
- LinkedList正相反,它随机访问集合元素的性能较差,因为需要遍历寻找index的位置,但是插入,删除操作很快,在找到元素位置后,只需修改前向指针和后向指针的指向即可。
三、总结
- 当需要频繁查询、有较多的随机访问的时候,使用ArrayList比较快;当需要频繁进行增删操作、有较少的随机访问的时候使用LinkList比较合适。
- LinkedList和ArrayList都只适用于单线程环境,对于高并发情况,JDK默认的线程安全的List是Vector。