LinkedList 初始化和基本增删操作
直接 new
LinkedList<t></t>就能创建空双向链表,内部节点自带前后引用,不用手动维护指针。插入和删除在任意位置都是 O(1),但前提是得先拿到目标节点(
LinkedListNode<t></t>),而不是靠索引——它不支持
[i]访问。
常见误用:想用
list.AddLast(x)后立刻
list[0]取值,会编译失败。正确做法是用
list.First.Value或
list.Last.Value拿头尾元素。
AddFirst()/
AddLast():插到头或尾,返回新节点(
LinkedListNode<t></t>)
AddBefore(node, value)/
AddAfter(node, value):必须传入已有节点,不是值或索引
Remove(node)或
Remove(value):前者快且稳定;后者会遍历找第一个匹配值,找不到不报错,只返回 false 清空用
Clear(),别用循环
RemoveFirst(),性能差
如何安全获取和遍历 LinkedList 节点
遍历推荐用
foreach直接枚举值:
foreach (var item in list) { ... }。如果需要修改结构(比如边遍历边删),就不能用 foreach,得手动走节点链。
容易踩坑的是节点生命周期:一个
LinkedListNode<t></t>只属于一个
LinkedList<t></t>实例。把它从 A 链表
Remove()后再塞进 B 链表,没问题;但如果没
Remove()就直接
AddLast(node)到另一个链表,会抛
InvalidOperationException:“The node belongs to a different LinkedList.” 从值找节点用
Find(value),返回
LinkedListNode<t></t>或 null 从节点取前后节点:用
node.Next/
node.Previous,为空时返回 null(不是异常) 遍历时删当前节点?必须先保存
node.Next,再调
list.Remove(node),否则
node.Next会变 null
LinkedList 和 List 的关键区别与选型建议
别因为“链表听起来高级”就默认选
LinkedList<t></t>。它的内存开销大(每个元素额外两个引用字段 + 节点对象),缓存不友好,遍历速度远慢于
List<t></t>。真正适合它的场景很窄: 频繁在头尾做插入/删除(比
List<t>.Insert(0, x)</t>或
RemoveAt(0)快得多) 需要在已知节点附近反复增删(比如实现 LRU 缓存,把刚访问的节点移到头) 不能接受
List<t></t>扩容时的数组复制开销,且确定不会随机访问
如果只是偶尔插中间、大部分时间查第 i 个元素,或者数据量小(List
常见错误:NullReferenceException 和 InvalidOperationException
两种异常最常出现在节点操作中:
NullReferenceException:对 null 节点调
node.Value或
node.Next,比如
Find()返回 null 后没判空就直接用
InvalidOperationException:“The LinkedList node does not belong to this LinkedList.”——节点被重复添加,或从原链表移除前就被加到别的链表
InvalidOperationException:“The LinkedList is empty.”——对空链表调
First/
Last属性(它们是 get-only,不抛异常,但值为 null;真要取值必须先判空)
安全写法是:所有通过
Find、
First、
Last拿到的节点,使用前先
if (node != null)。
