在go中的list表属于双向链表,表头部的prev和尾部的next同时指向一个相同的地址,从而实现了一个环。list的零值是一个准备使用的空列表。
源码:https://cs.opensource.google/go/go/+/refs/tags/go1.20.5:src/container/list/list.go
(资料图)
document:https://pkg.go.dev/container/list#pkg-overview
要使用list需要从container包引入。
list的方法
Next() : 返回下一个元素或nil
Prev(): 返回上一个元素或nil
Back(): 返回列表的最后一个元素,如果列表为空,则返回nil
Front(): 返回列表的首个元素,如果列表为空,则返回nil
Init(): 初始化或清空列表
InsertAfter(): 在某指定元素后插入
InsertBefore(): 在某指定元素前插入
Len(): 返回元素的个数,时间复杂度是O(1)
MoveToBack()、MoveToFront():将list中的某个元素移动到尾部/头部。如果元素不属于列表中的,则不会修改,元素必须不为nil。
MoveAfter(e,mark)、MoveBefore(e,mark): 将元素e移动到mark后面/前面,如果元素e或mark不属于list中的元素,或者e==mark,则不修改;e和mark不能是nil
PushBack()、PushFront()、Remove()见名知意
PushFrontList()、PushBackList(): 在列表的首部/尾部插入另外一个列表,他们不能为空。
方法示例:
package mainimport ( "container/list" "fmt")func main() { s := make([]int, 5) l := list.New() l2 := list.New().Init() fmt.Printf("%T %+[1]v %p\n", l, &l) fmt.Printf("%T %+[1]v %p\n", l2, &l2) l2.PushBack("gopher") l2.PushBack("come on") l.PushBack(2) l.PushFront("first") l.PushBack("你好吗") e := l.Front().Next() l.InsertAfter("c", e) l.InsertBefore(3.14, e) l.InsertBefore(s, e) l.PushBack("last") l.PushBackList(l2) // 将l2列表尾部加入到l列表中 e1 := l.Back().Prev().Prev() // 获取尾部的上一个的上一个元素 l.MoveToBack(e1) // 将“last”移动到尾部 l.MoveAfter(l.Back().Prev().Prev(), l.Front().Next()) // 将某个元素移动到某个元素后面 l.Remove(l.Front().Next()) // 移除某个元素 fmt.Printf("%T %+[1]v %p\n", l, &l) for i := l.Front(); i != nil; i = i.Next() { fmt.Printf("%T %[1]v\n", i) fmt.Println(i.Next()) }}示例0
list结构体
示例:*list.List &{root:{next:0xc000076510 prev:0xc000076510 list:
下面是一个示例
package mainimport ( "container/list" "fmt")func main() { s := make([]int, 5) l := list.New() fmt.Printf("%T %+[1]v %p\n", l, &l) l.PushBack(2) l.PushFront("first") l.PushBack("你好吗") e := l.Front().Next() l.InsertAfter("c", e) l.InsertBefore(3.14, e) l.InsertBefore(s, e) l.PushBack("last") fmt.Printf("%T %+[1]v %p\n", l, &l) for i := l.Front(); i != nil; i = i.Next() { fmt.Printf("%T %[1]v\n", i) }}示例1
我在执行了上面的代码后,返回是这样的:
*list.List &{root:{next:0xc000076510 prev:0xc000076510 list:Value: } len:0} 0xc00000a028*list.List &{root:{next:0xc0000765a0 prev:0xc000076690 list: Value: } len:7} 0xc00000a028*list.Element &{0xc000076630 0xc000076510 0xc000076510 first}*list.Element &{0xc000076660 0xc0000765a0 0xc000076510 3.14}*list.Element &{0xc000076570 0xc000076630 0xc000076510 [0 0 0 0 0]}*list.Element &{0xc000076600 0xc000076660 0xc000076510 2}*list.Element &{0xc0000765d0 0xc000076570 0xc000076510 99}*list.Element &{0xc000076690 0xc000076600 0xc000076510 你好吗}*list.Element &{0xc000076510 0xc0000765d0 0xc000076510 last}
从返回中,可以看到头部元素的prev和尾部元素的next都指向了0xc000076510 地址,通过这个地址完成了list的闭环。下面我使用图来表达,地址简写为后三位表达:
疑问:
1、上面示例中,在遍历元素的时候,发现每次都会打印0xc000076510这个指针,这是个什么指针?为什么每个元素都包含?
答:因为在使用list.New()来创建一个列表的时候,是创建了一个 list.List
实例,而该实例的所有元素都是通过该实例的指针进行访问和操作的。
具体来说,每个元素都是 list.Element
类型的结构体,其中包含了 Value
字段表示节点的值,以及 Next()
和 Prev()
方法表示指向下一个节点和上一个节点的指针。第三个指针就是表达指向该元素所在的 list.List
实例的指针。所以每次打印都会相同。