GO学习笔记(3)
三. 切片(slice )
切片:切片是数组的一个引用,因此切片是引用类型;切片是可变数组.
3.1. 切片定义
// 创建切片 len()切片的长度 cap()切片的容量 func sliceCreate() { // 声明 1 var s1 []int fmt.Println(reflect.TypeOf(s1)) // 查看类型: []int s2 := []string{} fmt.Println(reflect.TypeOf(s2)) // []string // 使用make声明 s3 := make([]int, 5, 8) fmt.Println(reflect.TypeOf(s3), s3, len(s3),cap(s3)) // []int [[0 0 0 0 0] 5 8 // 初始化 s4 := []int{1, 2, 3, 4, 5} fmt.Println(reflect.TypeOf(s4), s4, len(s4),cap(s4)) // []int [1 2 3 4 5] 5 5 // 从数组中切片 arr := [8]int{1,2,3,4,5,6,7,8} fmt.Println(reflect.TypeOf(arr), len(arr),cap(arr)) // [8]int 8 8 // s5的容量为7,是因为切片只是把起始位置的指针向后移动到start位置,而end位置是保持不变的,以下同理 s5 := arr[1:4] fmt.Println(s5, len(s5),cap(s5)) // [2 3 4] 3 7 s6 := arr[:] fmt.Println(s6, len(s6),cap(s6)) // [1 2 3 4 5 6 7 8] 8 8 s7 := arr[2:] fmt.Println(s7, len(s7),cap(s7)) // [3 4 5 6 7 8] 6 6 s8 := arr[:3] fmt.Println(s8, len(s8),cap(s8)) // [1 2 3] 3 8 s9 := s6[1:3] fmt.Println(s9, len(s9),cap(s9)) // [2 3] 2 7 }
3.2. 切片访问以及追加
func sliceAtc() {
// 可用指针直接访问底层数组
s1 := make([]int, 5, 8)
s1[1] = 2
p := &s1[3]
*p = 10
s2 := s1[1:]
s2[1] = 30
fmt.Println(s1) // [0 2 30 10 0]
// 可直接修改 struct array/slice 成员
d := [5]struct {
x int
}{}
s := d[:]
d[1].x = 10
s[2].x = 20
fmt.Println(d) // [{0} {10} {20} {0} {0}]
// s 是新开辟的空间,但是s指向的还是底层数组d
fmt.Printf("%p, %p, %p, %p\n", &s, &s[0], &d, &d[0]) // 0xc0000044e0, 0xc0000103c0, 0xc0000103c0, 0xc0000103c0
// 想切片中添加元素 append()
a := make([]int, 3, 5)
// 追加一个元素后,容量在a.cap()范围内,则底层数组不变,返回的slice依然指向原底层数组
b := append(a, 1)
fmt.Println(a, len(a), cap(a)) // [0 0 0] 3 5
fmt.Println(b, len(b), cap(b)) // [0 0 0 1] 4 5
// 超出b的容量,会重新分配底层数组
c := append(b, a...)
fmt.Println(c, len(c), cap(c)) // [0 0 0 1 0 0 0] 7 10
fmt.Println(&a[0], &b[0], &c[0]) // 0xc000010450 0xc000010450 0xc000014190
// 双冒号写法
slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
// 1. 常规切片
d1 := slice[6:8]
fmt.Println(d1, len(d1), cap(d1)) // [6 7] 2 4
// 2. 特殊写法: slice[x:y:z] 返回的切片长度为y-x,容量为z-x
d2 := slice[1:6:8]
fmt.Println(d2, len(d2), cap(d2)) // [1 2 3 4 5] 5 7
}
3.3. 切片拷贝
func sliceCopy() {
// copy到空slice
s1 := make([]int, 10)
s2 := []int{1, 2, 3, 4, 5}
copy(s1, s2)
fmt.Println(s1, &s1[0]) // [1 2 3 4 5 0 0 0 0 0] 0xc000014190
fmt.Println(s2, &s2[0]) // [1 2 3 4 5] 0xc0000103c0
s3 := append(s1, 1, 2, 3)
fmt.Println(s3, &s3[0]) // [1 2 3 4 5 0 0 0 0 0 1 2 3] 0xc00010e000
// 在两个 slice 间复制数据,复制长度以 len 小的为准
// 两个 slice 可指向同一底层数组,允许元素区间重叠
data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s4 := data[8:]
s5 := data[:5]
fmt.Println(s4) // [8 9]
fmt.Println(s5) // [0 1 2 3 4]
copy(s5, s4)
fmt.Println(s4) // [8 9]
fmt.Println(s5) // [8 9 2 3 4]
fmt.Println(data) // [8 9 2 3 4 5 6 7 8 9]
copy(s4,s5)
fmt.Println(s4) // [8 9]
}
3.4. 切片遍历
func eachSlice() { data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} for index, value := range data { fmt.Printf("inde : %v , value : %v\n", index, value) } }
3.5. 字符串切片
func sliceString() {
// 中文字符串直接切片可能会导致乱码,因为一个中文字符不止占一个字符
str1 := "GO:我的学习笔记"
s1 := str1[4:]
fmt.Println(s1) // ��习笔记
// 中文字符串必须先用rune强转,之后才能对字符串修改,英文字符串用byte()强转
s2 := []rune(str1)
s3 := s2[4:]
fmt.Println(s2, s3) // [71 79 58 25105 30340 23398 20064 31508 35760] [30340 23398 20064 31508 35760]
s2[3] = '你'
fmt.Println(s2) // [71 79 58 20320 30340 23398 20064 31508 35760]
fmt.Println(string(s2)) // GO:你的学习笔记
}
3.6. 切片扩容
func sliceAppend() { // 扩容时,返回的切片不一定都是新数组 // 容量超过1024, 返回新数组 s1 := make([]int, 1000) fmt.Printf("%p, %d, %d \n", &s1, len(s1), cap(s1)) // 0xc0000044a0, 1000, 1000 s2 := append(s1, 1) fmt.Printf("%p, %d, %d \n", &s2, len(s2), cap(s2)) // 0xc0000044e0, 1001, 2048 // 容量在1024内,扩容时容量翻倍,返回新数组 s3 := []int{1, 2, 3} s4 := append(s3, 5) s4[1] = 100 fmt.Printf("%p, %d, %d, %v \n", &s3, len(s3), cap(s3), s3) // 0xc000004500, 3, 3, [1 2 3] fmt.Printf("%p, %d, %d, %v \n", &s4, len(s4), cap(s4), s4) // 0xc000004520, 4, 6, [1 100 3 5] // 从数组中切片, 由于原数组还有容量可以扩容, 所以执行 append() 操作以后, 会在原数组上直接操作, // 所以这种情况下, 扩容以后的数组还是指向原来的数组, 修改切片元素就会影响到原数组 s5 := [8]int{1, 2, 3, 4, 5, 6, 7, 8} s6 := s5[:4] s7 := append(s6, 100) s7[2] = 200 fmt.Printf("%p, %d, %d , %v\n", &s5, len(s5), cap(s5), s5) // 0xc00000c340, 8, 8 , [1 2 200 4 100 6 7 8] fmt.Printf("%p, %d, %d , %v\n", &s6, len(s6), cap(s6), s6) // 0xc000004580, 4, 8 , [1 2 200 4] fmt.Printf("%p, %d, %d , %v\n", &s7, len(s7), cap(s7), s7) // 0xc0000045a0, 5, 8 , [1 2 200 4 100] }