1. slice:
go
①. 基础:
a. 切片是指向数组的一个引用,因此切片是引用类型.
b. 切片的长度可以改变.因此,切片是一个可变的数组.
c. 切片遍历方式和数组一样,可以用len()求长度.
②. 定义:
var 变量名 []类型 // 与数组的区别是没有写长度
a. var arr []string
③. len是长度、cap是容量:
a. cap表示slice最大的容量.
b. 大小关系:
0 <= len(slice) <= (array:slice引用的数组).
2. 通过数组创建切片:
go
var slice []int = arr[start:end] // 包含数组的start到end之间的元素(但不包含end)
var slice []int = arr[0:end] // 简写var slice []int = arr[:end]
var slice []int = arr[start:len(arr)] // 从start到末尾的所有元素,简写var slice []int = arr[start:]
var slice []int = arr[0, len(arr)] // 数组的所有元素,简写var slice []int = arr[:]
slice = slice[:len(slice)-1] // 切片最后一个元素去掉
(1). 数组创建切片的内存布局:
go
①. 如上图数据结构:
[ptr指针地址] [len长度] [cap]
类似:
type slice struct {
ptr *[5]int => 指向一个数组
len
cap
}
②. 验证最下面切片的ptr地址是指向数组的第1个元素的地址:
var a = [5]int{1, 2, 3, 4, 5}
b := a[1:5]
fmt.Println(b) // [2 3 4 5]
fmt.Printf("%p\n", b) // 0xc000076128
fmt.Println(&a[1]) // 0xc000076128
3. 通过make创建切片:
go
var a []type = make([]type, len)
a := make([]type, len)
a := make([]type, len, cap)
(1). make创建切片的内存布局:
①. 少了定义数组的过程,只不过它的底层内部创建了一个数组.
②. 两种底层都是一样在操作数组.
4. 内置函数append操作切片:
go
(1). 两个切片合并:
var a = []int{1, 2, 3}
var b = []int{4, 5, 6}
a = append(a, b...)
fmt.Println(a) // [1 2 3 4 5 6]
(2). 切片是动态的长度,如果超过固定的数组长度,会发生什么变化?
var a = [5]int{1, 2, 3, 4, 5}
b := a1[1:5]
fmt.Println(b) // [2 3 4 5]
fmt.Printf("%p\t%p\n", b, &a[1]) // 0xc00006c158 0xc00006c158
fmt.Printf("len:%d\tcap:%d\n", len(b), cap(b)) // len:4 cap:4
b = append(b, 6) // 多次追加后,导致切片的长度大于a数组的固定长度
fmt.Printf("len:%d\tcap:%d\n", len(b), cap(b)) // len:5 cap:8
b = append(b, 7)
fmt.Println(a) // [1 2 3 4 5]
fmt.Println(b) // [2 3 4 5 6 7]
fmt.Printf("%p\t%p\n", b, &a[1]) // 0xc00006e0c0 0xc00006c158
注:
①. 系统底层会将数组重新拷贝一份,重新生成一个新的数组指向.
②. 当追加超过cap时,自动会变成一倍容量:4 -> 8.
5. 切片拷贝:
go
s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, 1)
copy(s2, s1)
fmt.Println(s2) // [1],因为s2的cap是1.
s3 := []int{1, 2, 3}
s3 = append(s3, s2...)
s3 = append(s3, 4, 5, 6)
6. string原理:
string底层就是一个byte的数组,因此,也可以进行切片操作.
go
str := "helloworld"
a := str[0:5]
fmt.Println(a) // hello
b := str[5:]
fmt.Println(b) // world
(1). string的底层布局:
①. 字符串是由多个字符组成,字符串不能修改(不可变的).
a[0] = 'a' // 报错
②. [ptr指针地址] [len长度]
ptr指针指向一个byte数组
(2). 如何改变string中的字符值?
string本身是不可变的,因此要改变string中字符,需要如下操作:
go
str := "helloworld"
s := []byte(str) // 定义为[]rune(str)即可解决中文乱码问题
s[0] = 'o'
str = string(s)
fmt.Pintln(str) // oelloworld
7. 排序和查找操作:
排序操作主要在sort包中
go
import("sort")
①. sort.Ints:对整数进行排序.
var a = [...]int{2, 5, 3, 1, 7}
// 不能直接传a,是一个数组,值类型,就算里面排序好了,外面还是没有排序.要传一个切片.
sort.Ints(a[:])
②. sort.Strings:对字符串进行排序.
var a = [...]string{"abc", "ee", "ABC", "EFG", "vcd"}
sort.Strings(a)
③. sort.Float64s:对浮点数进行排序.
var a = [...]float64{20.88, 521.10, 56.20, 63.10}
sort.Float64(a)
(1). 用serach方法查找,必须是排好序的:
go
①. sort.SearchInts(a []int, b int):从数组a中查找b.
var a = [...]int{2, 5, 3, 1, 7}
sort.Ints(a[:])
sort.SearchInts(a, 5) // 前提a必须有序
②. sort.SearchFloats(a []float64, b float64):从数组a中查找b
③. sort.SearchStrings(a []string, b string) 从数组a中查找b