数组
- 声明和初始化
var array [5]int
var array [5]int{1, 2, 3, 4, 5}
array := [...]int{1, 2, 3, 4, 5}
array := [5]int{1: 20, 2:20}
- 访问指针数组的元素
// 用整型指针初始化索引为0和1的数组元素
array := [5]*int{0: new(int), 1: new(int)}
// 为索引为0和1的元素赋值
*array[0]=10
*array[1]=20
相同类型(数组长度和每个数组元素类型相同)的数组变量可以互相赋值。
- 二维数组
// 声明一个二维整型数组,两个维度分别存储 4 个元素和 2 个元素
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化外层数组中索引为 1 个和 3 的元素
array := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 声明并初始化外层数组和内层数组的单个元素
array := [4][2]int{1: {0: 20}, 3: {1: 41}}
- 函数间传递数组
由于Go语言函数之间传递变量是,总是按值传递。这就导致无论数组有多长,再传递给函数中是都会完整的拷贝一份,内存开销大。比较优雅的做法是,使用指针实现函数间传递数组。
// 分配一个8MB的数组
var array [1e6]int
// 将数组的地址传递给函数foo
foo(&array)
// 函数foo接受一个指向100万个整型值的数组的指针
func foo(array *[1e6]int) {
...
}
切片
- 切片的内部实现和基础功能
- 类似于动态数组,按需自动增长和缩小
- append函数实现动态增长,,cap返回容量(len返回长度,同样可应用于数组和通道)
- 切片可以多次切片
- 包含三个字段(指向底层数组的指针、切片访问的元素个数、切片允许增长到的元素个数)即地址指针、长度和容量
- 切片的创建和初始化
- 使用make创建
使用make创建切片时,需要传入一个参数指定切片的长度
// 创建一个字符串切片
// 其长度和容量都是 5 个元素
slice := make([]string, 5)
如果只指定长度,那么切片的容量和长度相等。可分别指定长度和容量,不允许创建容量小于长度的切片。
// 创建一个整型切片
// 其长度为 3 个元素,容量为 5 个元素
slice := make([]int, 3, 5)
- 使用切片字面量创建
切片字面量创建和数组类似,区别在于[]中是否指定了值,如果指定了,那就是数组,没有指定就是切片。
// 创建字符串切片
// 其长度和容量都是 5 个元素
slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
// 创建一个整型切片
// 其长度和容量都是 3 个元素
slice := []int{10, 20, 30}
// 创建字符串切片
// 使用空字符串初始化第 100 个元素
slice := []string{99: ""}
- nil和空切片
声明切片时不做任何初始化,就会创建一个nil的切片。在需要描述一个不存在的切片时,nil 切片会很好用。
// 创建nil整形切片
var slice []int
利用初始化,通过声明一个切片可以创建一个空切片。想表示空集合时空切片很有用.
// 使用 make 创建空的整型切片
slice := make([]int, 0)
// 使用切片字面量创建空的整型切片
slice := []int{}
- 创建和使用切片,一下代码获得的新的切片和原切片共享一个底层数组。
// 创建一个整型切片
// 其长度和容量都是 5 个元素
slice := []int{10, 20, 30, 40, 50}
// 创建一个新切片
// 其长度为 2 个元素,容量为 4 个元素
newSlice := slice[1:3]
// 输出为:20,30
对底层数组容量是 k 的切片 slice[i:j]来说
长度: j - i
容量: k - i
- 切片增长
// 创建一个整型切片
// 其长度和容量都是 5 个元素
slice := []int{10, 20, 30, 40, 50}
// 创建一个新切片
// 其长度为 2 个元素,容量为 4 个元素
newSlice := slice[1:3]
// 使用原有的容量来分配一个新元素
// 将新元素赋值为 60
newSlice = append(newSlice, 60)
如果底层数组没有足够的可用容量,append函数会创建一个新的底层数组(在切片的容量小于 1000 个元素时,总是会成倍地增加容量。一旦元素个数超过1000,容量的增长因子会设为 1.25,也就是会每次增加 25%的容量。随着语言的演化,这种增长算法可能会有所改变。
),将被引用的现有的值复制到新的数组里,再追加新的值。
// 创建一个整型切片
// 其长度和容量都是 4 个元素
slice := []int{10, 20, 30, 40}
// 向切片追加一个新元素
// 将新元素赋值为 50
newSlice := append(slice, 50)
- 创建切片时的3个索引
第三个索引可以用来控制切片的容量
// 创建字符串切片
// 其长度和容量都是 5 个元素
source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
// 将第三个元素切片,并限制容量
// 其长度为 1 个元素,容量为 2 个元素
slice := source[2:3:4]
对于 slice[i:j:k] 或 [2:3:4]
长度: j – i 或 3 - 2 = 1
容量: k – i 或 4 - 2 = 2
- 创建切片时拥有新的底层数组
如果在创建切片时设置切片的容量和长度一样,就可以强制让新切片的第一个 append 操作创建新的底层数组,与原有的底层数组分离。新切片与原有的底层数组分离后,可以安全地进行后续修改。
// 创建字符串切片
// 其长度和容量都是 5 个元素
source := []string{"Apple", "Orange", "Plum", "Banana", "Grape"}
// 对第三个元素做切片,并限制容量
// 其长度和容量都是 1 个元素
slice := source[2:3:3]
// 向 slice 追加新字符串
slice = append(slice, "Kiwi")
- append可变参数
append可以在一次调用传递多个追加的值,如果使用...运算符,可以将一个切片的所有元素追加到另一个切片里。
// 创建两个切片,并分别用两个整数进行初始化
s1 := []int{1, 2}
s2 := []int{3, 4}
// 将两个切片追加在一起,并显示结果
fmt.Printf("%v\n", append(s1, s2...))
Output:
[1 2 3 4]
- 迭代切片
使用range关键字迭代切片里面的元素。需要注意的是,关键字range返回两个值,第一个值是当前迭代的索引位置,第二个是该位置对应元素的一份副本(不是直接返回元素的引用,迭代过程中使用的是同一个地址不断赋值)。
同样可以使用传统的for循环迭代切片。
- 多维切片
// 创建一个整型切片的切片
slice := [][]int{{10}, {100, 200}}
- 在函数间传递切片
在64位架构的机器上,一个切片需要24字节的内存:指针字段需要8字节,长度和容量字段分别需要8字节。由于底层数组不属于切片本身,所以将切片复制到任意函数的时候,对底层数组大小都不会有影响。
// 分配包含 100 万个整型值的切片
slice := make([]int, 1e6)
// 将 slice 传递到函数 foo
slice = foo(slice)
// 函数 foo 接收一个整型切片,并返回这个切片
func foo(slice []int) []int {
...
return slice
}
映射
- 映射创建和初始化
- 使用make声明映射
// 创建一个映射,键的类型是 string,值的类型是 int
dict := make(map[string]int)
// 创建一个映射,键和值的类型都是 string
// 使用两个键值对初始化映射
dict := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
- 更常用映射字面量。映射的初始长度会根据初始化时指定的键值对的数量来确定。映射的键可以是内置的类型或结构类型,但不可以时具有引用语义的类型。
-
使用映射
- 为映射赋值
// 创建一个空映射,用来存储颜色以及颜色对应的十六进制代码 colors := map[string]string{} // 将 Red 的代码加入到映射 colors["Red"] = "#da1337"
- nil映射,通过声明一个为初始化的映射来创建一个nil的映射,nil映射不能用于键值对的存储。
-
从映射取值的两种操作
- 从映射获取值并且判断键是否存在
// 获取键 Blue 对应的值 value, exists := colors["Blue"] // 这个键存在吗? if exists { fmt.Println(value) }
- 值返回对应的值,并通过该值判断键是否存在
// 获取键 Blue 对应的值 value := colors["Blue"] // 这个键存在吗? if value != "" { fmt.Println(value) }
-
使用range迭代映射
range返回的是key、value。
// 创建一个映射,存储颜色以及颜色对应的十六进制代码
colors := map[string]string{
"AliceBlue": "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}
// 显示映射里的所有颜色
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
- 删除映射delete函数
// 删除键为 Coral 的键值对
delete(colors, "Coral")
// 显示映射里的所有颜色
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
- 在函数见传递映射
当传递映射给一个函数,不会制造出该映射的一个副本。但在对这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。
网友评论