
在 go 中遍历 `container/list` 时直接调用 `remove()` 会导致迭代中断,因为被删节点的 `next()` 返回 `nil`;正确做法是提前缓存 `e.next()` 到临时变量,再执行删除。
Go 的 container/list 是一个双向链表实现,其迭代逻辑与数组或切片不同:每个 *list.Element 的 Next() 方法返回下一个有效节点;一旦该节点被 l.Remove(e) 删除,其指针关系即被断开,后续调用 e.Next() 将不再可靠——尤其当 e 是当前最后一个非空节点时,e.Next() 直接返回 nil,导致 for 循环提前终止。
因此,标准且安全的“边遍历边删除”模式是:将 e.Next() 提前保存到局部变量(如 next),再更新 e = next。这样即使 e 被移除,也不会影响下一次迭代的起点。
以下是修正后的去重函数示例(已适配原问题需求):
func removeDuplicate(l *list.List) *list.List {
seen := make(map[int]bool) // 使用局部变量替代全局 sMap,更安全、可重入
var next *list.Element
for e := l.Front(); e != nil; e = next {
next = e.Next() // ✅ 关键:先保存下一个节点
if val, ok := e.Value.(int); ok {
if seen[val] {
fmt.Println("Deleting", val)
l.Remove(e)
} else {
fmt.Println("Keeping", val)
seen[val] = true
}
}
}
return l
}⚠️ 注意事项:
- 永远不要在循环条件中依赖被可能删除节点的 .Next():for e := l.Front(); e != nil; e = e.Next() 在 e 被 Remove() 后会失效;
- 使用局部 map 替代全局变量:避免并发风险与状态污染,提升函数可测试性与复用性;
- 类型断言需谨慎:生产代码中应检查 ok,避免 panic(本例假设数据类型严格为 int);
- 若需保留首次出现的元素(如去重),当前逻辑已满足;若需保留最后一次,则需反向遍历(从 Back() 开始)并配合 Prev()。
该模式不仅适用于去重,也适用于任意条件过滤(如删除负数、空字符串等),是操作 container/list 的基础安全范式。










