切片 Slice
切片,并不是数组或数组指针,它通过内部指针和相关属性引用数组片段,以实现变长方案。
- 切片是数组的一个引用,因此切片是引用类型,但自身是结构体,值拷贝传递
- 切片的长度是可以改变的,因此,切片是一个可变的数组
- 切片的遍历方式和数组一样,可以用len()求长度,表示可用元素数量,读写操作不能超过该限制
- cap可以求出slice最大的扩张容量,不能超出数组限制,0 <= len(slice) <= len(array)
- 定义:var 变量名 []类型
- 如果 slice == nil,那么 len,cap 结果都是 0
创建切片的几种方式:
package main
import (
"fmt"
)
func main() {
var s1 []int // 声明
s2 := []int{} // 声明+赋值,赋值为空
var s3 []int = make([]int,5) // len = cap = 5
var s4 []int = make([]int,2,5) // type len cap
s5 := []int{1,2,3} // 声明+赋值
arr := [5]int{1,2,3,4,5} // 对数组进行切片
var s6 []int
s6 = arr[1:4]
}
切片初始化
package main
import (
"fmt"
)
var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice0 []int = arr[2:8]
var slice1 []int = arr[0:6] //可以简写为 var slice []int = arr[:end]
var slice2 []int = arr[5:10] //可以简写为 var slice[]int = arr[start:]
var slice3 []int = arr[0:len(arr)] //var slice []int = arr[:]
var slice4 = arr[:len(arr)-1] //去掉切片的最后一个元素
func main() {
arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
slice5 := arr[2:8]
slice6 := arr[0:6] //可以简写为 slice := arr[:end]
slice7 := arr[5:10] //可以简写为 slice := arr[start:]
slice8 := arr[0:len(arr)] //slice := arr[:]
slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素
}
通过make,创建切片
package main
import (
"fmt"
)
// 全局
var slice0 []int = make([]int,5) // [0 0 0 0 0]
var slice1 = make([]int,5) // [0 0 0 0 0]
var slice2 = make([]int,2,5) // [0 0]
func main() {
// 局部
slice3 := make([]int,5) // [0 0 0 0 0]
slice4 := make([]int,2,5) // [0 0]
}
读写操作,实际目标是底层数组,只需要注意索引的差别
package main
import (
"fmt"
)
func main() {
data := [...]int{0,1,2,3,4,5}
s := data[2:4]
s[0] += 100
s[1] += 200
fmt.Println(s) // [102 203]
fmt.Println(data) // [0 1 102 203 4 5]
}
直接创建slice对象,自动分配底层数组
package main
import "fmt"
func main() {
s1 := []int{0, 1, 2, 3, 8: 100} // 索引为8,共9个元素
fmt.Println(s1, len(s1), cap(s1)) // [0 1 2 3 0 0 0 0 100] 9 9
s2 := make([]int, 6, 8) // len = 6 , cap = 8
fmt.Println(s2, len(s2), cap(s2)) // [0 0 0 0 0 0] 6 8
s3 := make([]int, 6) // len = cap = 6
fmt.Println(s3, len(s3), cap(s3)) // [0 0 0 0 0 0] 6 6
}
使用make动态创建slice,避免了数组必须用常量做长度的麻烦。还可以用指针直接访问底层数组,退化成普通数组操作。
package main
import "fmt"
func main() {
s := []int{0,1,2,3}
p := &s[2] // 获取数组元素的指针
*p += 100 // 重新赋值
// s[2] += 100
fmt.Println(s) // [0 1 102 3]
}
[][]T,是指 元素类型为 []T
package main
import "fmt"
func main() {
data := [][]int{
[]int{1,2,3},
[]int{100,200},
[]int{11,22,33,44},
}
fmt.Println(data) // [[1 2 3] [100 200] [11 22 33 44]]
}
直接修改 struct array/slice 成员
package main
import "fmt"
func main() {
d := [5]struct{
x int
}{}
s := d[:]
d[1].x = 10
s[2].x = 20
fmt.Println(d) // [{0} {10} {20} {0} {0}]
fmt.Printf("%p,%p,\n",&d,&d[0]) // 0xc042066030,0xc042066030,
}
切片基本操作,增删改查
package main
import "fmt"
func main() {
// 增
var a = []int{1,3,5}
var b = []int{2,4,6}
c := append(a,b...) // append 追加数据
fmt.Println(c) // [1 3 5 2 4 6]
d := append(c,9)
fmt.Println(d) // [1 3 5 2 4 6 9]
// 删 用append,以另一种形式实现删除操作
d = append(d[:3],d[4:]...)
fmt.Println(d) // [1 3 5 4 6 9] 删除第四个元素
// 改
// 用append删除一个元素,再用append增加一个元素
// 查
fmt.Println(d[1:3]) // [3 5]
// 清空
d = nil
fmt.Println(d) // []
}
向切片中添加元素,生成新切片
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 0, 5)
fmt.Printf("%p\n", &s1) // 0xc0420443a0
s2 := append(s1, 1)
fmt.Printf("%p\n", &s2) // 0xc0420443e0
fmt.Println(s1, s2) // [] [1]
}
超出原切片cap限制,就会重新分配底层数组,即便原数组并未填满。
package main
import (
"fmt"
)
func main() {
data := [...]int{0, 1, 2, 3}
s := data[:2:3] // 取data前两个元素,并设置cap为3
s = append(s, 100, 200) // append 两个值,超出 s.cap 限制。重新分配底层数组,与原数组无关。
fmt.Println(s, data) // [0 1 100 200] [0 1 2 3]
fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。 0xc04200a270 0xc042008320
}
从输出结果可以看出,append后的s重新分配了底层数据,并复制数据。如果只追加一个值,则不会超过 s.cap 限制,也就不会重新分配。通常以2倍容量重新分配底层数组。
在大批量添加数据时,建议一次性分配足够大的空间,以减少内存分配和数据复制的开销。或初始化足够长的len属性,改用索引号进行操作。及时释放不再使用的slice对象,避免持有过期数组,造成GC无法回收。
slice中cap重新分配规律:以2倍容量进行重新分配
package main
import (
"fmt"
)
func main() {
s := make([]int, 0, 1)
c := cap(s)
for i := 0; i < 50; i++ {
s = append(s, i)
if n := cap(s); n > c {
fmt.Printf("cap: %d -> %d\n", c, n)
c = n
}
}
}
// cap: 1 -> 2
// cap: 2 -> 4
// cap: 4 -> 8
// cap: 8 -> 16
// cap: 16 -> 32
// cap: 32 -> 64
遍历
package main
import "fmt"
func main() {
data := []int{10, 20, 30, 40, 50, 60, 70, 80, 90}
for index,val := range data {
fmt.Println(index,val)
}
}
调整大小
package main
import "fmt"
func main() {
var a = []int{1, 3, 4, 5}
fmt.Printf("slice a : %v , len(a) : %v\n", a, len(a))
b := a[1:2]
fmt.Printf("slice b : %v , len(b) : %v\n", b, len(b))
c := b[0:3]
fmt.Printf("slice c : %v , len(c) : %v\n", c, len(c))
}
// slice a : [1 3 4 5] , len(a) : 4
// slice b : [3] , len(b) : 1
// slice c : [3 4 5] , len(c) : 3
数组与切片,内存分布

字符串与切片
package main
import "fmt"
func main() {
str := "hello world"
// 字符串切片
s1 := str[:5]
fmt.Println(s1)
// 字符串 修改
s2 := []byte(str) //中文字符需要用[]rune(str)
s2[0] = 'H'
s2 = append(s2,'!')
s3 := string(s2)
fmt.Println(s3)
切片:的理解
data[6:8],取6和7,长度len=2,最大可扩容长度cap=4
data[:6:8],内容为0-5,长度len=6,cap=8
浙公网安备 33010602011771号