首页 > 编程笔记

Go语言列表用法详解

列表是一种非连续的存储容器,由多个节点组成,节点通过一些变量记录彼此之间的关系,列表有多种实现方法,如单链表、双链表等。

列表定义

列表的原理可以这样理解:假设 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() 的使用方式,具体说明如下:
运行上述代码,运行结果为:
列表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() 将元素移动到第一位

推荐阅读