container/list适用于频繁插入删除的动态序列。它通过List和Element实现双向链表,支持O(1)增删,但随机访问为O(n),适用于LRU缓存、可取消任务队列等场景。

Golang的
container/list
container/list
在使用
container/list
List
Element
List
Element
1. 初始化链表: 创建一个新的链表非常简单,通常我们直接调用
list.New()
package main
import (
"container/list"
"fmt"
)
func main() {
myList := list.New()
fmt.Printf("初始链表长度: %d\n", myList.Len()) // 输出: 初始链表长度: 0
}2. 添加元素:
container/list
PushFront(v interface{}) *ElementPushBack(v interface{}) *ElementInsertBefore(v interface{}, mark *Element) *Elementmark
InsertAfter(v interface{}, mark *Element) *Elementmark
这些方法都会返回新创建的
*Element
立即学习“go语言免费学习笔记(深入)”;
// 头部和尾部添加
myList.PushBack("世界") // 添加 "世界"
myList.PushFront("你好") // 添加 "你好" -> "世界"
fmt.Println("添加后:")
for e := myList.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value) // 输出: 你好 世界
}
fmt.Println()
// 插入到指定元素前后
first := myList.Front() // "你好"
myList.InsertAfter("Go", first) // "你好" -> "Go" -> "世界"
last := myList.Back() // "世界"
myList.InsertBefore("编程", last) // "你好" -> "Go" -> "编程" -> "世界"
fmt.Println("插入后:")
for e := myList.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value) // 输出: 你好 Go 编程 世界
}
fmt.Println()3. 遍历链表: 遍历链表通常从
Front()
Next()
Next()
nil
Back()
Prev()
fmt.Println("正向遍历:")
for e := myList.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value) // 输出: 你好 Go 编程 世界
}
fmt.Println()
fmt.Println("反向遍历:")
for e := myList.Back(); e != nil; e = e.Prev() {
fmt.Printf("%v ", e.Value) // 输出: 世界 编程 Go 你好
}
fmt.Println()4. 移除元素:
Remove(e *Element)
*Element
// 移除 "Go"
for e := myList.Front(); e != nil; e = e.Next() {
if e.Value == "Go" {
myList.Remove(e)
break // 移除后退出循环,因为e已经失效
}
}
fmt.Println("移除'Go'后:")
for e := myList.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value) // 输出: 你好 编程 世界
}
fmt.Println()
// 移除头部和尾部
myList.Remove(myList.Front()) // 移除 "你好"
myList.Remove(myList.Back()) // 移除 "世界"
fmt.Println("移除头部和尾部后:")
for e := myList.Front(); e != nil; e = e.Next() {
fmt.Printf("%v ", e.Value) // 输出: 编程
}
fmt.Println()
fmt.Printf("最终链表长度: %d\n", myList.Len()) // 输出: 最终链表长度: 15. 其他辅助方法:
Len() int
Front() *Element
Back() *Element
这些操作构成了
container/list
container/list
这其实是一个很经典的权衡问题,我个人在写Go代码时,大部分时间都会倾向于使用切片。但总有一些特定场景,让我不得不考虑
container/list
切片(
[]T
s[i]
而
container/list
Element
container/list
interface{}所以,我的经验是,如果你:
interface{}那么,
container/list
container/list
要理解
container/list
它主要由两个结构体构成:
List
type List struct {
root Element // sentinel list element; p.prev is last element, p.next is first // 哨兵元素
len int // current list length excluding sentinel element // 链表长度
}List
root
Element
root.prev
root.next
root.next
root.prev
root
nil
len
Element
type Element struct {
next, prev *Element // 前一个和后一个元素指针
list *List // 所属链表指针
Value interface{} // 实际存储的值
}每个
Element
next
prev
Element
List
List
Remove
Value
interface{}实现原理:
当你在链表中执行插入或删除操作时,其核心原理就是修改这些
next
prev
插入操作(例如InsertAfter
mark
newElement
mark
oldNext
newElement
prev
mark
newElement
next
oldNext
mark
next
newElement
oldNext
prev
newElement
删除操作(Remove
e
e
e.prev
e.next
e.prev
next
e.next
e.next
prev
e.prev
这种哨兵节点和双向指针的设计,使得链表在头部、尾部以及中间的插入和删除操作都能够保持极高的效率。但正如前面所说,这种设计也带来了额外的内存开销和随机访问的性能劣势。
container/list
虽然Go语言中切片和映射的使用频率远高于
container/list
container/list
LRU (Least Recently Used) 缓存实现: 这是
container/list
container/list
map
map[key] *list.Element
*list.List
实现自定义队列或栈,且需要支持中间元素的移除: 如果仅仅是FIFO队列(先进先出)或LIFO栈(后进先出),切片通常就足够了(
append
管理一个可重用的对象池: 在一些高性能服务中,为了避免频繁的对象创建和垃圾回收开销,会维护一个对象池。当需要一个对象时,从池中取出;使用完毕后,将对象放回池中。如果这个池需要支持快速地“借出”和“归还”对象,并且这些操作可能发生在池中的任何位置(比如,某个对象被标记为“损坏”需要移除),链表就可以很好地管理这些对象的生命周期。
事件处理系统中的事件队列: 在某些事件驱动的系统中,事件可能被添加到队列中等待处理。如果这些事件可能具有不同的优先级,或者某些事件在被处理前可能被取消,那么链表可以方便地实现这些动态的插入、移除和重新排序操作。
需要强调的是,尽管
container/list
container/list
以上就是Golang container/list库链表操作与实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号