Featured image of post Go 语言基础 数组、切片、映射

Go 语言基础 数组、切片、映射

在 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);
}

遍历映射的键值对时的顺序是随机,若要有序的获得映射的键值对,则需要先遍历出映射的键存到一个切片中,然后排序该切片,最后遍历该切片,按切片中元素的顺序去映射中取对应的值。