Go语言列表用法详解
列表是一种非连续的存储容器,由多个节点组成,节点通过一些变量记录彼此之间的关系,列表有多种实现方法,如单链表、双链表等。
图 1 单链表结构
在单链表结构的基础上,如果 C 把号码告诉 B,B 再告诉 A,这样就形成了双链表结构,如下图所示:
图 2 双链表结构
在 Go 语言中,列表使用内置包 container/list 实现,内部原理是双链表结构,能够高效地进行任意位置元素的插入和删除操作。列表定义有两种方式,分别是用关键字 var 和内置函数方法 new() 实现,示例如下:
运行上述代码,运行结果为:
如果删除列表中的某个元素,可以使用内置包 container/list 提供的函数 Remove(),但删除的变量必须为列表的元素信息,示例如下:
如果将 for 循环和函数 Remove() 结合使用,每次循环用于删除列表的某个元素,从而删除整个列表元素,示例如下:
上述代码将 for 的循环条件 i=i.Next() 改为 i=next,next 是 *list.Element 类型的变量,每次循环把变量 i 的值设为 i.Next(),从而获得下一次循环的列表元素,运行结果为:
因为调用函数 Remove() 会改变列表的元素结构,Go 语言为了避免内存泄漏,它默认将内置函数方法 Next() 和 Prev() 设置为 nil。如果不修改 for 的循环条件 i=i.Next(),当删除第一个元素之后,Remove() 就会将 Next() 和 Prev() 设置为 nil,从而终止整个 for 循环,程序也只会删除第一个元素。
在 for 循环中使用了内置包 container/list 的 Front() 和 Next(),除此之外,还可以使用 Back()、Prev()、MoveToBack() 和 MoveToFront(),函数说明如下表所示:
列表定义
列表的原理可以这样理解:假设 A、B、C 三个人都有电话号码,如果 A 把号码告诉 B,B 把号码告诉 C,这个过程就建立了一个单链表结构,如下图所示:图 1 单链表结构
在单链表结构的基础上,如果 C 把号码告诉 B,B 再告诉 A,这样就形成了双链表结构,如下图所示:
图 2 双链表结构
在 Go 语言中,列表使用内置包 container/list 实现,内部原理是双链表结构,能够高效地进行任意位置元素的插入和删除操作。列表定义有两种方式,分别是用关键字 var 和内置函数方法 new() 实现,示例如下:
// 关键字var定义列表 var name list.List // 使用内置函数方法new()定义 name := list.New()列表定义后,可以调用相关函数方法对列表执行元素的新增、插入、删除操作,并且列表元素没有限制数据类型。
列表操作
列表操作只有新增、插入和删除元素,内置包 container/list 提供了 6 个函数方法实现新增元素,使用方法如下:package main import ( "container/list" "fmt" ) func main() { // 定义列表变量 var l2 list.List fmt.Printf("列表l2:%v\n", l2) // 在列表末位新增元素,返回当前元素信息 l2.PushBack("a") // 在列表首位新增元素,返回当前元素信息 l2.PushFront(67) fmt.Printf("列表l2:%v\n", l2) // 定义列表变量 l1 := list.New() fmt.Printf("列表l1:%v\n", l1) // 在列表末位新增元素,返回当前元素信息 element := l1.PushBack("abc") fmt.Printf("元素element:%v\n", element) // 在元素element(即abc)后面添加元素 l1.InsertAfter("edf", element) fmt.Printf("在元素element后面添加元素:%v\n", l1) // 在元素element(即abc)前面添加元素 l1.InsertBefore("ghi", element) fmt.Printf("在元素element前面添加元素:%v\n", l1) // 列表l2的元素添加到列表l1的元素后面 l1.PushBackList(&l2) fmt.Printf("列表l2添加列表l1后面:%v\n", l1) // 将列表l2的元素添加到列表l1的元素前面 l1.PushFrontList(&l2) fmt.Printf("列表l2添加列表l1前面:%v\n", l1) }上面列举了函数方法 PushBack()、PushFront()、InsertAfter()、InsertBefore()、PushBackList()、PushFrontList() 的使用方式,具体说明如下:
- PushBack() 由列表变量调用,函数参数是新的元素值,它在列表末位添加新元素,函数返回值是当前添加的元素信息;
- PushFront() 由列表变量调用,函数参数是新的元素值,它在列表首位添加新元素,函数返回值是当前添加的元素信息;
- InsertAfter() 由列表变量调用,在列表中某个元素后面添加新的元素,函数第一个参数是新的元素,第二个参数是列表中某个元素;
- InsertBefore() 由列表变量调用,在列表中某个元素前面添加新的元素,函数第一个参数是新的元素,第二个参数是列表中某个元素;
- PushBackList() 由列表变量调用,将一个列表变量的元素添加到另一个列表变量的元素后面,函数第一个参数是另一个列表变量的内存地址;
- PushFrontList() 由列表变量调用,将一个列表变量的元素添加到另一个列表变量的元素前面,函数第一个参数是另一个列表变量的内存地址。
运行上述代码,运行结果为:
列表l2:{{<nil> <nil> <nil> <nil>} 0} 列表l1:{{0xc0000784800 0xc000078450 <nil> <nil>} 2} 列表l1:&{{0xc0000784e00 0xc0000784e0 <nil> <nil>} 0} 元素element: &{{0xc0000784e0 0xc0000784e0 0xc0000784e0 abc} 在元素element后面添加元素:&{{0xc0000785400 0xc0000785a0 <nil> <nil>} 2} 在元素element前面添加元素:&{{0xc0000786000 0xc0000785a0 <nil> <nil>} 3} 列表l1添加列表l2后面:&{{0xc0000786000 0xc000078690 <nil> <nil>} 5} 列表l2添加列表l1前面:&{{0xc0000787200 0xc000078690 <nil> <nil>} 7}
如果删除列表中的某个元素,可以使用内置包 container/list 提供的函数 Remove(),但删除的变量必须为列表的元素信息,示例如下:
package main import ( "container/list" "fmt" ) func main() { // 定义列表变量 var l2 list.List fmt.Printf("列表l2:%v\n", l2) // 添加元素 element := l2.PushBack("abc") fmt.Printf("列表l2:%v\n", l2) // 删除元素 l2.Remove(element) fmt.Printf("列表l2:%v\n", l2) }运行上述代码,运行结果为:
列表l2:{{<nil> <nil> <nil> <nil>} 0} 列表l2:{{0xc000078450 0xc000078450 <nil> <nil>} 1} 列表l2:{{0xc0000783f0 0xc0000783f0 <nil> <nil>} 0}
遍历列表元素
列表是双链表结构,如果遍历列表元素,就需要使用内置包 container/list 的 Front() 函数获取列表的首个元素,下一次遍历再调用内置包 container/list 的 Next() 函数获取下一个元素,直到列表元素等于空为止,即所有元素完成遍历,示例如下:package main import ( "container/list" "fmt" ) func main() { // 定义列表变量 var l2 list.List // 添加元素 l2.PushBack("Tom") l2.PushBack("Tim") l2.PushBack("Lily") l2.PushBack("Mary") // 遍历输出元素 for i := l2.Front(); i != nil; i = i.Next() { fmt.Printf("列表l2的元素是:%v\n", i.Value) } }每次循环列表的时候,当前循环信息是列表中某个元素的信息,若要取得列表元素的数值,则必须调用 Value 属性。上述代码运行结果为:
列表l2的元素是:Tom 列表l2的元素是:Tim 列表l2的元素是:Tily 列表l2的元素是:Mary
如果将 for 循环和函数 Remove() 结合使用,每次循环用于删除列表的某个元素,从而删除整个列表元素,示例如下:
package main import ( "container/list" "fmt" ) func main() { // 定义列表变量 var l2 list.List // 添加元素 l2.PushBack("Tom") l2.PushBack("Tim") l2.PushBack("Lily") l2.PushBack("Mary") // 定义变量next var next *list.Element // 遍历输出元素 for i := l2.Front(); i != nil; i = next { fmt.Printf("列表l2的元素是:%v\n", i.Value) // 设置变量next的值,用于执行下一次循环 next = i.Next() // 删除元素 l2.Remove(i) } fmt.Printf("列表l2的元素是:%v\n", l2) }
上述代码将 for 的循环条件 i=i.Next() 改为 i=next,next 是 *list.Element 类型的变量,每次循环把变量 i 的值设为 i.Next(),从而获得下一次循环的列表元素,运行结果为:
列表l2的元素是:Tom 列表l2的元素是:Tim 列表l2的元素是:Tily 列表l2的元素是:Mary 列表l2的元素是:{{0xc0000783f0 0xc0000783f0 <nil> <nil>} 0}
因为调用函数 Remove() 会改变列表的元素结构,Go 语言为了避免内存泄漏,它默认将内置函数方法 Next() 和 Prev() 设置为 nil。如果不修改 for 的循环条件 i=i.Next(),当删除第一个元素之后,Remove() 就会将 Next() 和 Prev() 设置为 nil,从而终止整个 for 循环,程序也只会删除第一个元素。
在 for 循环中使用了内置包 container/list 的 Front() 和 Next(),除此之外,还可以使用 Back()、Prev()、MoveToBack() 和 MoveToFront(),函数说明如下表所示:
函 数 | 说 明 |
---|---|
Front() | 获取列表的首个元素 |
Back() | 获取列表的最后一个元素 |
Next() | 获取下一个元素 |
Prev() | 获取上一个元素 |
MoveToBack() | 将元素移动到最后一位 |
MoveToFront() | 将元素移动到第一位 |