linkedlist 不能替代 list,因其底层为双向链表,索引访问不存在,遍历和内存效率更低,且操作易出错;仅当需在已知节点附近 o(1) 增删时才适用。

别用 LinkedList<t></t> 替代 List<t></t>,除非你明确需要在已知节点附近做 O(1) 增删,且不依赖索引访问。 它不是“更高级的 List”,而是用途极窄的专用结构——多数人用错,反而拖慢性能、增加内存开销、引发悬空节点等隐蔽问题。
为什么 LinkedList<t></t> 不能当 List<t></t> 用
根本原因在于底层模型完全不同:List<t></t> 是数组,LinkedList<t></t> 是双向链表。这导致:
-
list[i]在List<t></t>中是 O(1),在LinkedList<t></t>中根本不存在——编译报错 -
list.Find(x)看似简单,实则是 O(N) 遍历;而List<t>.IndexOf(x)</t>同样是 O(N),但后续若需改值或删它,List<t></t>的RemoveAt(i)仍是 O(N)(搬移),LinkedList<t></t>的Remove(node)才是真 O(1) ——但前提是 node 已经拿到 - 每个
LinkedListNode<t></t>额外占 16~24 字节(两个引用 + 对象头),List<t></t>每个元素只存值本身;缓存预取失效,实测遍历慢 2~5 倍 -
Clear()后,若外部还持有原LinkedListNode<t></t>引用,node.Next可能返回null或抛InvalidOperationException,行为未定义
AddFirst/AddLast 和 AddAfter/AddBefore 的关键区别
前两者是“值驱动”,后两者是“节点驱动”——这是理解所有操作的核心分水岭:
-
AddFirst("a")返回新节点LinkedListNode<string></string>,但你若不保存它,就再也没法快速定位这个 "a" -
AddAfter(node, "b")要求node必须属于当前链表,且不能为null;传入其他链表的节点会直接抛InvalidOperationException: "The node belongs to a different LinkedList." -
AddBefore(list.First, "x")等价于AddFirst("x"),但语义更明确;同理AddAfter(list.Last, "y")≡AddLast("y") - 想插到第 3 个位置?不能写
list.AddAfter(list.ElementAt(2), "z")——ElementAt是 O(N),整句变 O(N²);正确做法是手动从list.First走两次.Next
遍历时安全修改结构的唯一可靠方式
用 foreach (var item in list) 读值没问题,但一旦要删当前项,就会崩——因为枚举器内部持有一个快照式游标,结构变动后继续 MoveNext() 抛 InvalidOperationException。
- 必须手动遍历节点链:
var node = list.First;然后while (node != null) { var next = node.Next; if (ShouldRemove(node.Value)) list.Remove(node); node = next; } - 别写
for (int i = 0; i 配合 <code>list.ElementAt(i)——Count是 O(1),但每次ElementAt都从头开始走,复杂度爆炸 - 删完记得检查
node.Next是否为null,否则下一轮node = node.Next会 NRE - 如果要边遍历边把某些节点移到尾部(如 LRU),优先用
list.Remove(node); list.AddLast(node);,而不是重建新节点——复用原节点才能保 O(1)
Find 返回的是节点,不是值;改 node.Value 有陷阱
Find(x) 返回 LinkedListNode<t></t>,它的 Value 属性才是你要的数据。但直接赋值容易踩两类坑:
- 值类型(如
int):写node.Value = 42是改副本,原链表里该节点的值确实变了——这没问题;但若你误以为改了“引用”,回头又拿Find(42)想找它,可能因并发或重复值失败 - 引用类型(如
class Person):写node.Value.Name = "Alice"没问题;但写node.Value = new Person()是把节点指向一个新对象,原对象还在内存里,只是链表不再引用它——这不是“替换”,是“重绑定” - 最危险的是:你以为
list.Find(x).Value = y能批量更新所有匹配项,其实Find只返回第一个,且你没存住那个node,下次再Find就得重遍历
真正需要频繁按值修改时,说明数据组织方式错了——该用字典配链表(如 Dictionary<tkey linkedlistnode>></tkey>),而不是反复 Find。










