1. Golang 数组和切片

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