java-Linkedlist源码分析

深入分析 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` 字段分别指向链表的首节点和尾节点,这样可以高效地进行头部和尾部的插入和删除操作。

相关推荐
It'sMyGo几秒前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
睡觉然后上课11 分钟前
c基础面试题
c语言·开发语言·c++·面试
qing_04060318 分钟前
C++——继承
开发语言·c++·继承
武昌库里写JAVA19 分钟前
【Java】Java面试题笔试
c语言·开发语言·数据结构·算法·二维数组
ya888g19 分钟前
GESP C++四级样题卷
java·c++·算法
【D'accumulation】30 分钟前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
小叶学C++36 分钟前
【C++】类与对象(下)
java·开发语言·c++
ac-er888837 分钟前
PHP“===”的意义
开发语言·php
2401_8543910839 分钟前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端
Cikiss1 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务