数组

数组是编程语言中最常用的数据结构,Go语言中声明数组的方式如下:

var arr [3]int
var ptr_arr [10]*float64
var multi_arr [3][5]int

注意,数组长度在定义后就不可在更改,在声明时长度可以为一个常量或者一个常量表达式。

获取数组的长度使用内置函数len()来获取。

元素访问

使用数组下标访问,例如arr[0]获取数组中第一个元素,与C语言类似。

数组赋值

var arr [5]int = [5]int{1, 2, 3, 4, 5}

数组遍历

最基础的类似C语言遍历方式。

var arr [5]int = [5]int{1, 2, 3, 4, 5}
for i :=0; i < len(arr); i++ {
    fmt.Println(arr[i]);
}

Go语言提供了一个关键字range,用于遍历容器中的元素。

for i, v := range arr {
    fmt.Println(i, v);
}

注意,在Go语言中数组是一个值类型,所有的值类型变量在赋值和作为参数传递时将产生一次复制动作。不像C语言数组的默认的是第一个元素的指针。

数组切片

创建数组切片

基于数组

var myArray [5]int = [5]int{1, 2, 3, 4, 5}  // 定义数组
var mySlice []int = myArray[:3]

fmt.Println(mySlice)    // [1, 2, 3]

支持myArray[first:last]方式来生成一个数组切片。

基于数组所有元素创建数组切片:mySlice = myArray[:]

基于数组前3个元素创建数组切片:mySlice = myArray[:3]

基于从第5个元素开始所有元素创建数组切片:mySlice = myArray[5:]

直接创建

var mySlice []int   // 声明一个数组切片
mySlice := make([]int, 5)   // 使用内置函数make()创建数组切片

用内置函数make()创建一个初始元素个数为5的数组切片,用法如下,

元素初始值为0:mySlice := make([]int, 5)

元素初始值为0,并预留10个元素的存储空间:mySlice := make([]int, 5, 10)

直接创建并初始化:mySlice := []int{1, 2, 3, 4 ,5}

基于数组切片创建

mySlice := []int{1, 2, 3, 4, 5}
newSlice := mySlice[:3]

元素遍历同数组的遍历方式。

动态增减元素

数组切片支持内置cap()len()函数。

cap()函数返回的是数组切片分配的空间大小。
len()函数返回的是数组切片中当前所存储的元素个数。

mySlice := make([]int, 5, 10)
fmt.Println(cap(mySlice), len(mySlice)) // 10 5

如果向已经包含5个元素后面继续增加新增元素,可以使用append()函数。

mySlice = append(mySlice, 1, 2, 3)
fmt.Println()       // [0 0 0 0 0 1 2 3]

也可以将一个数组切片追加到另一个数组切片的末尾,

mySlice2 := []int{1, 2, 3}
mySlice = append(mySlice, mySlice2...)  // 后面三个点(省略号)不能省略,否则会编译错误。

// 类似于
mySlice = append(mySlice, 1, 2, 3)
内容复制

数组切片内容复制使用内置函数copy(),用于将内容从一个数组切片复制到另一个数组切片。如果两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。

mySlice1 := []int{1, 2, 3, 4, 5}
mySlice2 := []int{7, 8, 9}

copy(mySlice1, mySlice2)    // 只会复制mySlice2的3个元素到mySlice1的前3个位置
// mySlice1 输出 [7, 8, 9, 4, 5]

copy(mySlice2, mySlice1)    // 只会复制mySlice1的前三个元素到mySlice2中
// mySlice2 输出 [1, 2, 3]

map

map是一堆键值对的未排序集合。可以理解类似json的数据结构。

map声明

格式:var newMap map[string] int

其中,newMap是声明的map变量名,string是键的类型,int是所存放的值类型。

也可以使用内置函数make()来创建一个map

var myMap map[string] int   // 声明一个map
myMap = make(map[string]int, 10)    // 指定该map具有初始10 int的存储能力

// 声明一个myMap2变量,并自动推导成map类型
myMap2 := make(map[string]int)  

// 声明map并初始化。
var myMap3 = map[string]int {"php":1, "go": 2}

元素赋值

元素赋值就是键和值用下面的方式对应起来即可。

myMap["go"] = 1

注意一点就是,声明的空map不能直接赋值。

var myMap = map[string]int
myMap["go"] = 1     // 编译报错 panic: assignment to entry in nil map

var newMap = map[string]int {"php":1}
newMap["Go"] = 2    // 编译通过

另外,map键可以是任意内置类型或者struct类型,slicefunction和包含slicestruct类型不可以作为map键。

dict := map[[]string] int // 编译错误

元素删除

delete()内置函数,用于删除容器内的元素,无论键是否存在都编译通过,但是nil的map会抛出异常。

var rank = map[string]int{"php": 1, "go": 2, "c": 3}
delete(rank, "php")
fmt.Println(rank)       // map[go:2 c:3]

元素查找

要从map中查找一个特定的键,可以以下方式实现,

value, ok := myMap["go"]

if ok {
    // 找到了value
}

遍历

同采用range的数组遍历方式。

var colors = map[string] string {}
colors["red"] = "#ff0000"
colors["green"] = "#00ff00"

for i, v := range colors {
    fmt.Println(i, v)
}

判断是否成功找到特定的键,不需要检查取到的值是否为nil,只需查看第二个返回值ok。

判断map是否为空map

在Go语言中。可以用内置len()判断元素个数。

var dict = map[string] int {}   // 声明一个map并初始化为空

fmt.Println(len(dict))  // 输出 0

Map在函数间传递,不是map值拷贝,而是值引用,所以引用map的地方都会改变。

func modify(colors map[string] string) {
    colors["red"] = "#f00000"
}

var colors = map[string] string{"red": "#ff0000"}
modify(colors)

fmt.Println(colors)     // 输出 map[red:#f00000]

总结一下数组(Array)、数组切片(slice)和Map区别:

数组的长度是固定并且不可以改变的,数组切片(slice)的长度是可变的,而Map的长度类似于数组切片的形式, 简单看一下三者的声明。

var arr [4]int      // 数组的声明方式
var mySlice []int       // 切片的声明方式
var dict map[string]int     // map的声明方式

数组(Array)在函数间传递是按值传递,而数组切片(slice)和Map在函数之间传递是值引用,只有改变Map的元素,原来的map或slice就会跟着改变。

数组(Array)和数组切片(Slice)是不同的类型。切记在函数间传递的时候,注意下参数的类型。

func modify(colors []int) {
    // 实现代码
}

func main() {
    var color [2]string = {"#000000", "#ffffff"}
    modify(color)  // 编译错误 cannot use colors (type [2]string) as type []string in argument to modify
}