读注释
首先咱们来看看源码里的一些注释说明:
Doubly-linked list implementation of the {@code List} and {@code Deque} interfaces. Implements all optional list operations, and permits all elements (including {@code null})
意思就是LinkedList是一个双向链表,实现了List接口和Deque接口。实现了所有的可选的list操作,允许包括null在内的所有元素。
All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index
所有的操作都与双向链表相同,索引链表中的元素时将会从链表头结点或者尾节点开始,这要取决于要索引的元素距离哪边比较近。
ote that this implementation is not synchronized. If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally.
接下来说的是LinkedList并不是同步的,如果多个线程并发的去访问一个链表,并且至少有一个线程对其进行改变,我们必须在外部进行加锁。我们可以使用如下方法来创建安全的链表:
List list = Collections.synchronizedList(new LinkedList(...))
The iterators returned by this class’s {@code iterator} and {@code listIterator} methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator’s own {@code remove} or {@code add} methods, the iterator will throw a {@link ConcurrentModificationException}.
由列表的iterator和listIterator方法返回的迭代器是快速失败的,通俗点说就是:如果在迭代器生成之后,除了调用迭代器的remove方法或add方法对链表进行修改的操作之外的任何其他方式的修改,都会导致迭代器抛出ConcurrentModificationException异常。也就是说,迭代器生成之后,你可以通过迭代器的remove或add方法去对列表进行修改,不能再调用链表的自身的add或remove方法对链表进行修改。
读源码
首先看看类的头部
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
正如注释里面说的,LinkedList是实现了List和Deque的双向链接,链表的节点使用的是静态内部类:
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;
}
}
可以很明显的看到,确实是一个双向链表,每个节点都有指向前一个和后一个节点的指针。
构造函数
public LinkedList() {}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
有参构造函数可以接受其他类型的集合,如ArrayList
获取首尾元素
// 获取头结点元素
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
// 获取尾节点元素
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
这里要注意的是如果链表里没有元素,那么会抛出NoSuchElementException异常
移除首尾节点
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
准确的说,这两个方法应该是返回并移除首尾节点,类似的,如果链表为空也会抛出NoSuchElementException异常,我们进去看看unlinkFirst(f)的实现:
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
熟悉链表结构的其实这里一看就明白,首先获取要移除的元素f,暂存为element,然后拿到f的下一个元素,将f的next指针置空,有利于垃圾回收;将链表的头指针指向f.next,如果链表只有一个元素,直接把链表尾结点置空,如果多于一个元素,则把f.next.prev置空,这样就把要移除的元素彻底从链表中断开。最后记录链表长度,修改次数。
在头尾处添加元素
// Inserts the specified element at the beginning of this list.
public void addFirst(E e) {
linkFirst(e);
}
// Inserts the specified element at the end of this list.
public void addLast(E e) {
linkLast(e);
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
我们看看在头部添加元素,首先暂存头结点为f,然后将头结点first指向新添加的节点,如果链表为null,那么尾结点也指向新节点,不为null则将原来的头结点的prev指向新节点。注意这里在构造新节点的时候已经将新节点的next指向了原来的头结点。
链表是否包含某一元素
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
判断链表是否包含某一元素分为两类,若待判断元素为null和不为null,包含就返回对应的索引值,否则返回-1。
添加或移除元素
// Appends the specified element to the End of this list.
public boolean add(E e) {
linkLast(e);
return true;
}
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
add()方法等同于上面的addLast(),在链表末尾添加元素。移除某一元素与判断链表是否包含某一元素有点类似,也是分为两种情况,待移除元素为null和不为null。如果相同的元素有多个,则移除遇到的第一个。移除操作很简单,无非是将移除的节点前后指针调整一下,释放掉该结点就行了。
模拟进栈出栈 or 进队出队操作
// Inserts the specified element at the front of this list.
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
// Inserts the specified element at the end of this list.
public boolean offerLast(E e) {
addLast(e);
return true;
}
// Retrieves, but does not remove, the first element of this list
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
// Retrieves, but does not remove, the last element of this list
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
// Retrieves and removes the first element of this list
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
// Retrieves and removes the last element of this list
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
// 或者用一以下两个模拟栈操作,内部使用的方法跟offerFirst一样
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
以上方法的组合使用可以模拟进栈出栈 或 队列的相关操作。
链表转化为数组
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}