LinkedList是基于链表结构的集合,所以随机访问比较慢,插入删除元素很快:
LinkedList 代码如下:
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
如上可以看到LinkedList是实现了List接口和继承了AbstractSequentialList类,从JDK的源代码中可以看到LinkedList并不是线程安全的,可以通过以下方法创建一个线程安全的List:
List list = Collections.synchronizedList(new LinkedList(...));
接下来我们分析LinkedList的元素以及作用:
LinkedList有三个内部变量:
transient int size = 0;
transient Node first;
transient Node last;
- size是元素的个数
- first是指向第一个元素的指针
- last是指向最后一个元素的指针
在LinkedList中元素被封装为Node节点
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
可以看到Node是一个双向节点,每个节点都保存它的前驱节点和后驱节点。
LinkedList中的私有方法分析:
linkFirst方法,将元素添加到头节点,代码如下:
private void linkFirst(E e) {
final Node f = first; //保存头结点
//创建一个新节点,前驱节点为空,后驱节点为当前头结点,
final Node newNode = new Node<>(null, e, f);
//头结点指向当前新节点
first = newNode;
//如果头结点为空的话
if (f == null)
//将last节点指向该节点
last = newNode;
else
//将头结点的前驱节点设为当前节点
f.prev = newNode;
//数量加1
size++;
//修改次数加1
modCount++;
}
linkLast方法是将元素添加到链表的最后,代码如下:
void linkLast(E e) {
//获取最后一个节点
final Node l = last;
//创建新节点,前驱节点为last节点,后驱节点为null
final Node newNode = new Node<>(l, e, null);
//将last节点指向新的节点
last = newNode;
if (l == null)
//如果最后节点为空,那么头结点指向新节点
first = newNode;
else
//不为空,最后节点的后驱节点是新节点
l.next = newNode;
//元素数量加1
size++;
//修改次数加1
modCount++;
}
linkBefore方法是在节点前插入数据:
void linkBefore(E e, Node succ) {
//获取要被插入位置的节点的前驱节点
final Node pred = succ.prev;
//创建新节点,前驱节点是pred,后驱节点是原来节点succ
final Node newNode = new Node<>(pred, e, succ);
//将原来节点succ的前驱节点设置为新节点
succ.prev = newNode;
if (pred == null)
//如果为空的话,头结点指向新节点
first = newNode;
else
//原来节点的前驱节点指向新节点
pred.next = newNode;
//数量加1
size++;
//修改次数加1
modCount++;
}
unlinkFirst方法删除头节点:
private E unlinkFirst(Node f) {
//传入的头节点
final E element = f.item;
//获取到头结点的后驱节点
final Node next = f.next;
//将头结点的值设置为空
f.item = null;
//后驱节点设置为空
f.next = null; // help GC
//头结点指向后驱节点
first = next;
//如果头结点后驱节点为空
if (next == null)
//最后一个节点也是空
last = null;
else
//头结点的后驱节点的前一个节点为空,因为该节点成为了头节点
next.prev = null;
//数量减1
size--;
//修改次数加1
modCount++;
return element;
}
公用方法是提供给外部调用的下面分析公用public方法
public boolean add(E e)方法将元素添加到链表的尾部:
public boolean add(E e) {
linkLast(e); //直接调用我们上面分析的linkLast方法
return true;
}
add(int index, E element)方法将元素添加到对应的位置上
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element); //如果是插入到链表尾,直接调用linkLast
else
linkBefore(element, node(index)); //调用linkBefore方法
}
addAll(int index, Collection extends E> c)方法将链表添加到另一个链表尾部:
public boolean addAll(int index, Collection extends E> c) {
//检查是否越界
checkPositionIndex(index);
//将要添加的集合转化为数组
Object[] a = c.toArray();
//数组的长度
int numNew = a.length;
//如果数组长度为0说明没有要添加的数组
if (numNew == 0)
return false;
//定义两个节点
Node pred, succ;
//如果index刚好等于原list长度的话
if (index == size) {
//succ节点设置为空
succ = null;
//pred节点指向last
pred = last;
} else {
//将Index的节点指向succ
succ = node(index);
//pred指向succ前驱节点
pred = succ.prev;
}
//遍历数组
for (Object o : a) {
Node newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
remove()方法是删除链表头节点:
public E remove() {
//直接删除头节点
return removeFirst();
}
总结:
LinkedList是双向链表,可以存入null
插入和删除元素效率很高,时间复杂度是O(1),但是根据索引访问元素效率低