容器
数组
var 数组变量名 [数组长度]Type
数组长度也是数组类型的一部分,[3]int 和 [4]int 是两种不同的数组类型
默认情况下,数组每个元素都会被初始化为元素类型对应的零值,也可以自己初始化数组var a [3]int = {1,2,3}
q := [...]int{1,2,3}表示数组长度根据初始化
a = [3]int{1:2,2:-1}可以这样为指定索引初始化
如果两个数组类型相同(包括数组的长度,数组中元素的类型)的情况下,我们可以直接通过较运算符(==和!=)来判断两个数组是否相等
slice
slice 是一个拥有相同元素的可变长度序列。 slice 看起来与数组很像,但本质上不同。slice依赖于数组
有三个属性
- 底层数组指针(指针指向数组的某个元素,不一定是第一个,这是slice可以访问的第一个元素,对切片的修改会影响底层数组)
- 长度
- 容量
每个切片指向一个底层数组,数组容量够用就添加新增元素,不够用切片会扩容,此时切片指向的底层数组也会更换
切片默认指向一段连续内存区域,可以是数组,也可以是切片本身。
从连续内存区域生成切片是常见的操作,格式如下:
slice[开始位置:结束位置] //包头不包尾
从数组生成新切片
var a = [3]int{1, 2, 3}
fmt.Println(a, a[1:2])
a[:] // 生成的切片将表示和原切片一致的切片
直接声明新的切片
var name []Type
切片是动态结构,只能和nil比较,不能互相比较,声明新的切片后,可以使用 append() 函数向切片中添加元素。
使用make()构造切片
make([]Type,size,cap)
cap的设定不影响size,只是能提前分配空间降低多次分配空间造成的性能问题
func main() {
a := make([]int,3,6)
// a[0] = 1
// a[1] = 2
// a[2] = 3
a = append(a,1)
a = append(a,2)
a = append(a,3)
a = append(a,4)
fmt.Println(a) // [0 0 0 1 2 3]
fmt.Println(len(a),cap(a)) // 7 12
}
可看出切片容量不够会扩容两倍
使用 make() 函数生成的切片一定发生了内存分配操作,但给定开始与结束位置(包括切片复位)的切片只是将新的切片结构指向已经分配好的内存区域,设定开始与结束位置,不会发生内存分配操作。
Go语言的内建函数 append() 可以为切片动态添加元素,代码如下所示:
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
a = append(a,1,2,3) // 在切片尾部添加元素
a = append([]int{1,2,3},a...) // 在切片头部添加元素
在切片开头添加元素一般都会导致内存的重新分配,而且会导致已有元素全部被复制 1 次,因此,从切片的开头添加元素的性能要比从尾部追加元素的性能差很多。
通过append函数的组合,可以实现在切片中插入元素
func main() {
a := make([]int,3,6)
i := 3
x := 9
a = append(a[:i],append([]int{x},a[i:]...)...)
fmt.Println(a) // [0 0 0 9]
}
range关键字
可以配合for来遍历切片,数组,字符串等
关键字 range 会返回两个值,第一个值是当前迭代到的索引位置,第二个值是该位置对应元素值的一份==副本==
map
map 是引用类型,可以使用如下方式声明:
var mapName map[keyType]valueType
make(map[KeyType]ValueType)
使用函数 len() 可以获取 map 中 pair 的数目
使用for range遍历同时获取键和值
map是无序的
delete(map,键):从map中删除键值对
sync.Map
并发环境中使用的map
- 不能使用map的方式进行取值和设置等操作,调用他的方法进行操作,Store 表示存储,Load 表示获取,Delete 表示删除。
- 使用 Range 配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,Range 参数中回调函数的返回值在需要继续迭代遍历时,返回 true,终止迭代遍历时,返回 false。
package main
import (
"fmt"
"sync"
)
func main() {
var scene sync.Map
// 将键值对保存到sync.Map
scene.Store("greece", 97)
scene.Store("london", 100)
scene.Store("egypt", 200)
// 从sync.Map中根据键取值
fmt.Println(scene.Load("london"))
// 根据键删除对应的键值对
scene.Delete("london")
// 遍历所有sync.Map中的键值对
scene.Range(func(k, v interface{}) bool {
fmt.Println("iterate:", k, v)
return true
})
}
list
在Go语言中,列表使用 container/list 包来实现,内部的实现原理是双链表,列表能够高效地进行任意位置的元素插入和删除操作。
列表与切片和 map 不同的是,列表并没有具体元素类型的限制,因此,列表的元素可以是任意类型,这既带来了便利,也引来一些问题,例如给列表中放入了一个 interface{} 类型的值,取出值后,如果要将 interface{} 转换为其他类型将会发生宕机。
创建
import "container/list"
var l1 list.List
l := list.New()
操作
列表插入函数的返回值会提供一个 *list.Element 结构,这个结构记录着列表元素的值以及与其他节点之间的关系等信息,从列表中删除元素时,需要用到这个结构进行快速删除。
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
ele := l.PushBack("back")
element := l.PushFront("first")
l.InsertAfter(22, element)
l.InsertBefore(33, ele)
l.Remove(ele) // 删除back
for i := l.Front(); i != nil; i = i.Next() {
fmt.Println(i.Value)
}
}