1.1. 数组
数组初始化方式常用的有 3 种,至于其它的用的很少,就不用管了,常用方式如下:
var a[4]intb := [4]int{2, 4}
c := [...]int{2, 4}
Go 数组是值类型,赋值和传参会复制整个数组数据,为了避免数据复制,可以使用数组指针:
func test(x *[2]int) {
x[1] += 1
}
func main() {
a := [2]int{2, 3}
test(&a)
}
最后需要区分指针数组和数组指针的区别:
func main() {
x, y := 1, 2
a := [...]*int{&x, &y} // 元素为指针的指针数组
p := &a // 存储数组地址的指针
}
Go 的数组,其实我们用的不多,一般大家都用切片,所以对于数组,掌握上述知识就可以了,其它关于数组的知识,需要用的时候,查阅相关资料即可。
1.2. 切片
1.2.1. 概念
切片出现的原因也是因为数组的可操作性不高。切片的长度是不固定的,可以追加数据,可以理解切片是一个动态数组,切片的底层是一个结构体。
type slice struct {
array unsafe.Pointer
len int
cap int
}
slice
1.2.2. 切片创建
make
普通格式:
var 切片名 [] 数据类型
自动推导类型创建切片:
切片名 := [] 类型{}
make
// 长度是不能大于容量的,容量可以省略不写,不写时候就默认和长度的值一样
切片名称 := make ([] 切片类型,长度 容量)
// 返回切片的容量使用 cap, 返回切片的长度使用 len
fmt.Println(cap(切片名称))
// 演示
s1 := make([]int, 3, 5) // 指定 len、cap, 底层数组初始化为零值
s2 := make([]int, 3) // 省略 cap, 和 len 相等
s3 := []int{10, 20, 5: 30} // 按初始化元素分配底层数组,并设置 len、cap, 设置索引 5 的数据为 30
1.2.3. 切片初始化
append
// 普通格式创建的切片
切片名 [索引] = 值
// 自动推导类型创建的切片
切片名 := [] 类型{数据 1, 数据 2, 数据 3}
// make 函数方式创建的切片可以通过 append 和循环初始化
切片名称 = append(切片名称,数据 1, 数据 2...)
// 演示
s1 := make([]int, 4, 6) // 由于 `len = 4`, 所以后面 2 个暂时访问不到,但是容量还是在,数组里面每个变量都是 0。
s2 := []int{10,20,30,40,50,60}
1.2.4. append 函数
appendslice(len)10241/4appendappendappend
1.2.5. copy 函数
0len-1copy
格式:
copy(切片 1, 切片 2)
演示:
// 从切片 2 复制到切片 1, 但是切片 2 的数据比切片 1 的多,所以,最终只是复制了一部分,也就是索引相对应的数据
func main() {
slice := []int{1, 2, 3}
slice2 := []int{4, 5, 6, 7, 8, 9}
copy(slice, slice2)
fmt.Println(slice) // [4 5 6]
}
// 从切片 1 复制到切片 1, 但是切片 1 的数据比切片 2 的少,所以,最终只是复制了一部分,也就是索引相对应的数据
func main() {
slice := []int{1, 2, 3}
slice2 := []int{4, 5, 6, 7, 8, 9}
copy(slice2, slice)
fmt.Println(slice2) // [1 2 3 7 8 9]
}
[]byte
func main() {
b := make([]byte, 3)
n := copy(b, "abcde")
fmt.Println(n, b)
}
1.2.6. 切片截取
切片截取就是从切片中获取指定的数据。如果初始化切片时,没有指定切片的容量,切片容量是跟随原切片的。
切片截取的操作:
s[n]sns[:]s0len(s)-1s[low:]slen(s)-1s[:high]s0len=highs[low:high]slen=high-lows[low:high:max]slen=high-low, cap=max-lowlen(s)s<=cap(s)cap(s)s>=len(s)
/**
第一个值:截取的起始索引
第二个值:截取的终止索引(不包括该值)
第三个值:用来计算切片的容量,可以省略,默认和长度一样
容量 = 第三个值 - 第一个值
长度 = 第二个值 - 第一个值
*/
newSlice := slice[0:3:3] // 切片的操作符 `s[i:j:k]`, `j` 和 `k` 是个开区间。
1.2.7. 切片值的修改
切片截取后返回新切片,对新切片的值进行修改,会影响原来的切片。
原因:切片截取后新的切片,不会给新的切片是指向了原来的切片,没有给新的切片开辟新的空间,所以对于新的切片操作会影响到原来的切片。
1.2.8. nil 和空切片
nilnil
var slice []int
空切片一般会用来表示一个空的集合。比如数据库查询,一条结果也没有查到,那么就可以返回一个空切片。
silce := make([]int , 0) slice := []int{}
nilappendlencapnil
1.2.9. 切片扩容
10241.251/4
func main() {
array := [4]int{10, 20, 30, 40}
slice := array[0:2] // 10 20
newSlice := append(slice, 50) // 10 20 50
newSlice[1] += 10 // 10 30 50
// 这里 slice=[10 30], array=[10 30 50 40], 入坑!! !
fmt.Printf("slice = %v\n", slice) // [10 30]
fmt.Printf("array = %v\n", array) // [10 30 50 40]
fmt.Printf("newSlice = %v\n", newSlice) // [10 30 50]
}
slicenewSlicearraynewSlice[1]
1.2.10. 切片遍历
forrange
// 演示
func main() {
slice := []int{1, 2, 3, 4, 5}
for i := 0; i < len(slice); i++ {
fmt.Print(slice[i])
}
for _, v := range slice {
fmt.Println(v)
}
}
range
1.2.11. 切片作为函数参数
切片可以做为函数的参数,但是在函数中修改切片的值,会影响到原切片。
因为切片的底层是结构体,结构体里有个参数 Pointer, Pointer 会指向切片的内存地址,使用的是浅拷贝方式,所以会影响到原切片值。
func main() {
slice := []int{1, 2, 3, 4, 5}
SliceDemo10(slice)
}
func SliceDemo10(slice []int) {
for _, v := range slice {
fmt.Println(v)
}
slice = append(slice, 5, 6, 7)
fmt.Println(slice)
}