2. 容器

2.1 数组

声明

var name [元素数量]T

例如:

var a [3]int
var r [3]int = [3]int{1, 2}
p := [...]int{1, 2, 3}    // ...表示数组的长度是根据初始化值的个数来计算
q := [...]int{3: 3, 5: 5} // 长度为 6,并且 q[3] = 3, q[5] = 5,其他都为默认值 0

比较两个数组

如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过 == 比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符 != 遵循同样的规则。

遍历数组

var team [3]string
team[0] = "hammer"
team[1] = "soldier"
team[2] = "mum"

for k, v := range team {
    fmt.Println(k, v)
}

// 0 hammer
// 1 soldier
// 2 mum

2.2 切片

从数组或切片生成新的切片

// slice[开始位置:结束位置]
var a  = [3]int{1, 2, 3}
fmt.Println(a, a[:], a[1:2])// [1 2 3] [1 2 3] [2]

直接声明新的切片

// var name []T // 和数组声明就差了元素个数不用声明
var numList []int
var numListEmpty = []int{}

切片是动态结构,只能与 nil 比较。

声明新的切片后,可以使用 append() 函数来添加元素。

使用 make() 函数构造切片

// make([]T, size, cap)
a := make([]int, 2)
b := make([]int, 2, 10)

对于切片,函数 len 返回切片的长度,函数 cap 返回切片的容量。

增加元素

var a []int
a = append(a, 1)                                 // 追加1个元素
a = append(a, 1, 2, 3)                           // 追加多个元素, 手写解包方式
a = append(a, []int{1, 2, 3}...)                 // 追加一个切片, 切片需要解包
a = append([]int{0}, a...)                       // 在开头添加1个元素
a = append([]int{-3, -2, -1}, a...)              // 在开头添加1个切片
a = append(a[:i], append([]int{x}, a[i:]...)...) // 在第i个位置插入x

切片在扩容时,容量的扩展规律按容量的 2 倍数扩充,例如 1、2、4、8、16……。

复制

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置

copy 函数的第一个参数是要复制的目标 slice,第二个参数是源 slice,copy 函数将返回成功复制的元素的个数

删除元素

从开头删除

// 移动数据指针
a = a[1:] // 删除开头1个元素
a = a[N:] // 删除开头N个元素

// 不移动数据指针
a = append(a[:0], a[1:]...) // 删除开头1个元素
a = append(a[:0], a[N:]...) // 删除开头N个元素

// 用 copy 完成删除开头的元素:
a = a[:copy(a, a[1:])] // 删除开头1个元素
a = a[:copy(a, a[N:])] // 删除开头N个元素

从中间删除

a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
// 同样可以用 append 或 copy 原地完成

从尾部删除

a = a[:len(a)-1] // 删除尾部1个元素
a = a[:len(a)-N] // 删除尾部N个元素

遍历(等同于数组)

2.3 range 关键字

range 可以配合关键字 for 来迭代容器。

当迭代切片时,关键字 range 会返回两个值。第一个值是当前迭代到的索引位置,第二个值是该位置对应元素值的一份副本

2.4 map

声明

// var name map[keyType]valueType
var map1 map[string]int

初始化

// name := make(map[keyType]valueType[, cap]) 或 name := map[keyType]valueType{}
map0 := make(map[string]int, 2)
map1 := map[string]int{"one": 1, "two": 2}

增加元素

map0[key] = value

删除元素

delete(map, key)
// 清空 map 的话直接 make 一个新的 map 即可。

遍历

for k, v := range map {
    fmt.Println(k, v)
}

2.5 sync.Map

map 在并发情况下,只读是线程安全的,同时读写线程不安全,Go 语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,演示代码如下:

var syncMap0 sync.Map
syncMap0.Store("one", 1)
syncMap0.Store("two", 2)

// 从sync.Map中根据键取值
fmt.Println(syncMap0.Load("one"))

// 根据键删除对应的键值对
syncMap0.Delete("one")

syncMap0.Range(func(key, value interface{}) bool {
    fmt.Println(key, value)
    return true
})
// sync.Map 没有提供获取 map 数量的方法,替代方法是获取时遍历自行计算数量。

2.6 list

初始化

var l0 list.List
l1 := list.New()
// 两种效果一样

列表与切片和 map 不同的是,列表并没有具体元素类型的限制。因此,列表的元素可以是任意类型。

增加元素

l.PushFront("front") // 从队列前方插入元素
l.PushBack("back")   // 从队列后方插入元素

axis := l.PushBack("axis")
l.InsertAfter("axis + 1", axis)  // 在 axis 之后添加 axis + 1
l.InsertBefore("axis - 1", axis) // 在 axis 之前添加 axis - 1

删除元素

l.Remove(element)

遍历

for i := l.Front(); i != nil; i = i.Next() {
    fmt.Println(i.Value)
}

results matching ""

    No results matching ""