一、概述
LinkedList<T> 是 .NET 标准库(System.Collections.Generic 命名空间)中提供的一种泛型双向链表实现。
其本质特征如下:
- 采用 双向链表(Doubly Linked List) 结构
- 每个节点通过引用连接前驱节点和后继节点
- 不支持基于索引的随机访问
- 提供高效的节点级插入与删除操作
二、类定义与继承体系
csharp
public class LinkedList<T> :
ICollection<T>,
ICollection,
IReadOnlyCollection<T>,
IEnumerable<T>,
IEnumerable,
ISerializable,
IDeserializationCallback
关键说明:
-
不实现
IList<T>- 表明其不支持索引访问(如
list[0])
- 表明其不支持索引访问(如
-
提供集合基础能力(枚举、计数、复制等)
-
支持序列化与反序列化
三、内部数据结构
1. 节点结构:LinkedListNode<T>
csharp
public sealed class LinkedListNode<T>
{
public T Value { get; set; }
public LinkedListNode<T>? Next { get; }
public LinkedListNode<T>? Previous { get; }
public LinkedList<T>? List { get; }
}
特点:
-
每个节点同时保存:
- 当前值
Value - 前驱节点
Previous - 后继节点
Next
- 当前值
-
节点与链表实例绑定,不能在多个链表之间共享
2. 链表整体结构
-
维护:
- 头节点
First - 尾节点
Last - 元素数量
Count
- 头节点
-
内部逻辑结构为 首尾相连的双向循环链表(实现细节)
四、核心属性
| 属性 | 说明 |
|---|---|
First |
获取第一个节点 |
Last |
获取最后一个节点 |
Count |
元素数量 |
csharp
LinkedList<int> list = new LinkedList<int>();
LinkedListNode<int>? first = list.First;
五、核心方法分类
1. 添加元素
基于值的添加
csharp
AddFirst(T value)
AddLast(T value)
基于节点的添加
csharp
AddBefore(LinkedListNode<T> node, T value)
AddAfter(LinkedListNode<T> node, T value)
示例
csharp
var list = new LinkedList<int>();
var node = list.AddLast(10);
list.AddAfter(node, 20);
2. 删除元素
csharp
Remove(T value)
Remove(LinkedListNode<T> node)
RemoveFirst()
RemoveLast()
Clear()
注意事项:
Remove(T value)删除的是第一个匹配项- 删除节点操作为 O(1)(前提是已持有节点引用)
3. 查找元素
csharp
Find(T value)
FindLast(T value)
- 返回
LinkedListNode<T>? - 查找时间复杂度为 O(n)
4. 枚举遍历
csharp
foreach (var item in list)
{
// 顺序遍历
}
- 遍历顺序:从
First到Last - 枚举期间修改集合会抛出
InvalidOperationException
六、时间与空间复杂度分析
| 操作 | 时间复杂度 |
|---|---|
| 头/尾插入 | O(1) |
| 已知节点插入 | O(1) |
| 已知节点删除 | O(1) |
| 查找 | O(n) |
| 遍历 | O(n) |
空间开销
- 每个节点额外维护两个引用(
Previous、Next) - 相比数组型集合,内存占用更高
七、使用规范与注意事项
1. 何时应使用 LinkedList<T>
适用场景:
- 频繁插入和删除
- 操作对象是"节点位置",而非索引
- 需要在遍历过程中进行结构修改
2. 不适合的场景
不推荐用于:
- 高频随机访问
- 仅进行尾部追加(
List<T>性能更优) - 数据量较小且结构简单的集合
3. 节点使用规范
❌ 错误示例(跨链表使用节点):
csharp
var list1 = new LinkedList<int>();
var list2 = new LinkedList<int>();
var node = list1.AddLast(1);
list2.AddLast(node); // 抛异常
✅ 正确做法:
- 每个
LinkedListNode<T>只能属于一个链表
八、与 List<T> 的关键差异
| 维度 | LinkedList | List |
|---|---|---|
| 底层结构 | 双向链表 | 动态数组 |
| 随机访问 | 不支持 | 支持 |
| 插入删除 | 高效 | 中间位置代价高 |
| 内存占用 | 较高 | 较低 |
| 常用性 | 较低 | 非常高 |
九、设计层面的总结
-
LinkedList<T>是面向节点操作的集合 -
其价值在于:
- 常量时间的结构性修改
- 明确的前驱 / 后继关系
-
在现代 .NET 应用中属于特定场景下的工具型集合,而非通用首选