linkedlist的addbefore/addafter要求节点必须属于当前链表,否则抛invalidoperationexception;next/previous为null表示无后继/前驱,非哨兵;遍历时删除须手动迭代并暂存next;remove(t)依赖equals比较,引用类型需重写该方法。

为什么直接用 LinkedList<t></t> 的 AddBefore 和 AddAfter 会抛 InvalidOperationException
因为这两个方法要求传入的 LinkedListNode<t></t> 必须属于当前链表。如果节点是新 new 出来的,或来自别的链表,运行时就直接炸——不是空引用,而是明确告诉你“该节点不属于此链表”。常见于想“先造节点再插入”的写法:
var node = new LinkedListNode<int>(42); // ❌ 错误:孤立节点 list.AddLast(node); // ✅ 可以(自动归属) list.AddBefore(someNode, node); // ❌ 报 InvalidOperationException</int>正确做法永远是让链表自己创建节点:用
AddBefore / AddAfter 传值,而不是传节点;或用 AddFirst/AddLast 接收返回的节点再操作。
LinkedListNode<t></t> 的 Next 和 Previous 为空时代表什么
它们是 null,不是哨兵节点,也不指向头/尾。这意味着:
- 遍历时必须显式判空,不能假设
node.Next != null就一定有下个有效值 - 删除节点后,其
Next和Previous不会被清空,但已脱离链表——此时访问它们不会报错,但属于悬空引用,逻辑上无意义 - 从外部拿到一个
LinkedListNode<t></t>,仅靠Next == null无法判断它是否是尾节点(可能只是刚被移除)
node.List?.Last == node;同理,头节点用 node.List?.First == node。
在循环中删除当前遍历到的节点,为什么不能用 foreach
foreach 底层调用 Enumerator,而任何结构修改(包括 Remove)都会使枚举器失效,立刻抛 InvalidOperationException: Collection was modified。必须改用手动迭代:
var node = list.First;
while (node != null)
{
var next = node.Next; // 先存下一个,避免删除后断链
if (ShouldRemove(node.Value))
list.Remove(node);
node = next;
}注意:不能写成 node = node.Next 后再删,因为 Remove 会把 node.Next 置为 null,导致丢失后续节点。
自定义类型做 LinkedList<t></t> 元素时,Remove 操作为何找不到节点
因为 Remove(T value) 是按值查找,依赖 EqualityComparer<t>.Default</t>。如果 T 是引用类型且未重写 Equals 和 GetHashCode,默认比较的是引用地址——哪怕两个对象内容完全一样,只要不是同一个实例,就查不到。
- 解决方法一:重写
Equals/GetHashCode(推荐) - 解决方法二:不用
Remove(T),改用Find+Remove(LinkedListNode<t>)</t>,自行实现查找逻辑 - 解决方法三:用
RemoveAll配合谓词,绕过相等性比较
list.RemoveAll(x => x.Id == targetId) 比 list.Remove(targetObj) 更可控、更不易出错。
双向链表的“双向”特性只在节点处于链表中时才成立;一旦节点被移除或未加入,它的 Next/Previous 就只是残留字段,不能再当作链式结构的一部分来推理。










