在 Go 语言中,为便于存储及管理用户数据,其数据结构设计分为数组 Array、切片 Slice、映射 Map 三种结构。
近期又看了 Go 语言基础的内容,看了一下这三种结构实现的原理:
数组 Array
- 数组是切片和映射的基础数据结构;
- 数组是长度固定的数据类型并且在内存中也是连续分配的,固索引数组数据速度是非常快的;
- 声明数组时需要指定数组存储的类型及数量(数组的长度);
- 数组变量的类型包括数组长度和元素的类型,只有两部分都相同的数组才可相互赋值。
创建及初始化
一旦声明了数组,其本身的数据类型及长度都是不可以进行变更。
1
2
3
4
5
6
7
8
9
|
// 使用数组字面量声明数组
array := [5]int{1, 2, 3, 4, 5}
// 自动推导长度声明数组
array := [...]int{1, 2, 3, 4, 5, 6}
// 使用 ... 代替长度,根据初始化元素个数推导
// 声明数组并指定特定元素值
array := [5]int{1:10, 2:20}
|
指针类型
数组元素的类型可以为任何内置类型,也可以是某种结构类型,也可以是指针类型。
1
2
3
4
5
6
7
|
// 声明一个元素长度为 3 的指向字符串的指针数组
var array1 [3]*string
// 为指针数组指定元素
*array1[0] = "demo0"
*array1[1] = "demo1"
*array1[2] = "demo2"
|
多维数组
数组本身是一维数据,多维数组是由多个数组组合而来的。
1
2
3
4
5
6
|
// 声明一个二维数组
var array = [3][2]int
// 声明了一个两个维度为 3 和 2 的元素
// 初始化二维数组
var array = [3][2]int{ {1, 2}, {3, 4}, {5, 6}}
|
在函数间传递数组:由于在函数间传递变量时,传递的总是变量的值的副本,所以在传递数组变量时将拷贝整个数组!在定义函数时,对于较大的数据类型应该把参数设计为指针类型,这样在调用函数时,只需在栈上分配给每个指针8字节的内存,但这意味着会改变指针指向的值(共享的内存),其实大部分情况下应该使用切片类型,而不是数组。
切片 Slice
- 切片 slice 是引用类型,它引用了其指针字段所指向的底层数组的一部分或全部;
- 切片是围绕动态数组的概念构建的;
- 切片的动态增长是通过 append 来实现的;
- 缩小则是通过对它再次切片来实现,通过再次切片获得的新切片将和原切片共享底层数组,它们的指针指向同一个底层数组。
创建及初始化
切片类型有3个字段:
- 指针:指向切片所包含的第一个元素在底层数组中的地址;
- 长度:切片所包含的底层数组的元素的个数(切片可访问的元素的个数);
- 容量:切片允许增长到的最大元素个数,即底层数组的长度。
make 和切片字面量
1
2
3
4
5
6
|
// 使用 make 创建一个切片
slice := make([]int, 3)
// 创建一个具有长度和容量的切片
slice := make([]int, 1, 6)
// 长度为 1,容量为 6 个元素
|
nil 和空切片
1
2
3
4
5
6
|
// nil 字符串切片
var slice []string
// 空切片
slice := []int{}
// 空的整形切片
|
由于切片只是引用了底层数组,底层数组的数据并不属于切片本身,所以一个切片只需要 24字节的内存(在 64位机器上):指针字段 8字节、长度字段 8字节、容量字段 8字节。所以在函数之间直接传递切片是高效的,只需分配 24字节的栈内存。
len
函数可返还切片的长度、cap
函数可返还切片的容量。
映射 Map
- 映射 map 是用来存储一系列的无序键值对;
- 映射是无序的集合,其实现使用了散列表;
- 映射的散列表包含一组桶,每个桶里存储着一部分键值对;
- 映射内部使用了两个数组:
- 第一个数组:存储着用于选择桶的散列键的高八位值,该数组用于区分每个键值对要存在哪个桶里;
- 第二个数组:每个桶里都有一个字节数组,先依次存储了该桶里的所有键,之后存储了该桶的所有值;
创建及初始化
1
2
3
4
5
6
7
8
9
10
11
12
|
// 创建一个映射 存储学生信息
students := map[string]string{
"name" : "mengxiaoyu",
"age" : "22",
"sex" : "boy",
"hobby": "pingpang",
}
// 显示映射所有信息
for key, value := range students{
fmt.printf("key:%s, \t value:%s\n", key, value);
}
|
遍历映射的键值对时的顺序是随机,若要有序的获得映射的键值对,则需要先遍历出映射的键存到一个切片中,然后排序该切片,最后遍历该切片,按切片中元素的顺序去映射中取对应的值。