深入分析 Java 中的 `LinkedList` 源码
`LinkedList` 是 Java 集合框架中的一个重要类,它基于双向链表实现,提供了高效的插入和删除操作。与 `ArrayList` 不同,`LinkedList` 的结构使其在特定操作上有更优的性能表现。本文将详细分析 `LinkedList` 的源码,包括其数据结构、构造方法、核心操作等。
1. `LinkedList` 的基本数据结构
`LinkedList` 是基于双向链表实现的,这意味着每个节点都包含对前一个节点和后一个节点的引用。`LinkedList` 主要由以下几个关键字段组成:
```java
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
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;
}
}
}
```
-
`size`:链表中的元素数量。
-
`first`:指向链表的第一个节点。
-
`last`:指向链表的最后一个节点。
-
`Node`:链表节点的内部类,每个节点包含元素数据和前后节点的引用。
2. 构造方法
`LinkedList` 提供了多个构造方法:
2.1 默认构造方法
```java
public LinkedList() {
}
```
默认构造方法初始化一个空的链表。
2.2 从另一个集合创建 `LinkedList`
```java
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
```
此构造方法从另一个集合创建 `LinkedList`,并将集合中的所有元素添加到链表中。
3. 核心操作方法
3.1 添加元素
`LinkedList` 提供了多种添加元素的方法:
3.1.1 在链表末尾添加元素
```java
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++;
}
```
-
`add(E e)`:在链表末尾添加元素。
-
`linkLast(E e)`:将新节点链接到链表的末尾。如果链表为空,则新节点既是 `first` 也是 `last`。
3.1.2 在指定位置插入元素
```java
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
```
-
`add(int index, E element)`:在指定位置插入元素。
-
`linkBefore(E e, Node<E> succ)`:在指定节点之前插入新节点。
-
`node(int index)`:返回指定位置的节点。
3.1.3 添加元素到链表的头部
```java
public void addFirst(E e) {
linkFirst(e);
}
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++;
}
```
-
`addFirst(E e)`:在链表头部添加元素。
-
`linkFirst(E e)`:将新节点链接到链表的头部。如果链表为空,则新节点既是 `first` 也是 `last`。
3.2 删除元素
`LinkedList` 提供了多种删除元素的方法:
3.2.1 删除指定位置的元素
```java
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node<E> x) {
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;
}
```
-
`remove(int index)`:删除指定位置的元素。
-
`unlink(Node<E> x)`:断开指定节点的链接,并返回节点中的元素。
3.2.2 删除链表的头部元素
```java
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
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;
}
```
-
`removeFirst()`:删除链表的头部元素。
-
`unlinkFirst(Node<E> f)`:断开头部节点的链接,并返回节点中的元素。
3.2.3 删除链表的尾部元素
```java
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
```
-
`removeLast()`:删除链表的尾部元素。
-
`unlinkLast(Node<E> l)`:断开尾部节点的链接,并返回节点中的元素。
3.3 获取元素和修改元素
`LinkedList` 提供了获取和修改元素的方法:
3.3.1 获取元素
```java
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
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)`:根据索引获取元素。
-
`node(int index)`:返回指定位置的节点,通过判断索引位置决定从前往后还是从后往前遍历,提高效率。
3.3.2 修改元素
```java
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
```
- `set(int index, E element)`:根据索引修改元素,并返回旧元素。
4. 双向链表的结构特点
4.1 链表节点的定义
在 `LinkedList` 中,每个节点包含一个元素和对前后节点的引用:
```java
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;
}
}
```
-
`item`:存储节点的元素。
-
`next`:指向下一个节点。
-
`prev`:指向前一个节点。
4.2 链表的首尾节点
`LinkedList` 通过 `first` 和 `last` 字段分别指向链表的首节点和尾节点,这样可以高效地进行头部和尾部的插入和删除操作。