一、简介

LingkedList 是 Java 集合框架中一个常用的类,它实现了 ListQueueSerializable 接口。底层使用 双向链表 作为数据结构。

特点:

特性 描述
增删操作快 插入、删除节点时不需要移动元素,效率高
查询效率低 查询元素需要从头或尾部一个个遍历
线程不安全 多线程环境下需手动同步或使用 Collections.synchronizedList 包装

二、底层结构

LinkedList 的核心是一个内部静态类 Node<E> :

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;
}
}

成员变量

transient int size = 0;
transient Node<E> first;
transient Node<E> last;

  • first:指向链表的头结点
  • last:指向链表的尾结点
  • size:链表元素个数

整个链表通过节点的 nextprev 字段形成双向连接。

三、常用方法

add(E e)

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++;
}

将元素添加到链表末尾,如果链表为空,则 firstlast 都指向新节点。

remove()

public E remove() {
return removeFirst();
}

public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();

//调用 unlinkFirst(f) 断开节点与链表的联系并返回其值
return unlinkFirst(f);
}

private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;

// 清除当前头结点的引用,帮助 GC 回收
f.item = null;
f.next = null;

// 更新链表的 first 指针
first = next;

if (next == null)
last = null; // 链表已经为空
else
next.prev = null; // 去除旧头结点的前向引用

size--;
return element;
}

从链表头部删除元素,同时更新 first 指针。

get(int index)、node(int index)

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}

Node<E> node(int index) {
// 如果 index < size / 2,则从前往后找;否则从后往前找(提高效率)
// 折中优化策略
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;
}
}

查询时,LinkedList 会根据索引判断从头还是从尾开始遍历,优化性能。

安全校验

private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}

在访问元素之前,会先校验是否越界,防止访问非法位置导致程序崩溃。

四、注意事项

  • 不要使用 LinkedList 来频繁随机访问元素。
  • 如果是栈、队列或双端队列操作,LinkedList 是一个很好的选择。
  • Java 8 之后建议使用 ArrayDeque 替代 LinkedList 实现队列,性能更优。

总结

  • LinkedList 是基于 双向链表 实现的,适用于 频繁插入删除 的场景。
  • 每个节点包含三个属性:当前元素、前一个节点引用、后一个节点引用。
  • 查询操作性能相对较低,但可以通过判断位置优化遍历方向。