每天进步一点点!
一、概念
切片(Slice)是一个拥有相同类型元素的可变长度的序列
二、声明
(一) 只声明类型不初始化赋值,值为nil
func main() {
// 声明切片类型
var a []string //声明一个字符串切片
fmt.Println(a) //[]
fmt.Println(a == nil) //true
(二) 声明类型并初始化
func main() {
// 声明切片类型
var b = []int{} //声明一个整型切片并初始化
var c = []bool{false, true} //声明一个布尔切片并初始化
fmt.Println(b) //[]
fmt.Println(b == nil) //false
fmt.Println(c) //[false true]
fmt.Println(c == nil) //false
}
三、注意事项
(一) 切片是引用类型,不支持直接比较,只能和nil比较
func main() {
// 声明切片类型
var c = []bool{false, true} //声明一个布尔切片并初始化
var d = []bool{false, true} //声明一个布尔切片并初始化
// fmt.Println(c == d) //切片是引用类型,不支持直接比较,只能和nil比较
}
(二) 切片的底层就是一个数组,所以我们可以基于数组通过切片表达式得到切片。
- 切片表达式中的low和high表示一个索引范围(左包含,右不包含);
- 也就是下面代码中从数组a中选出1<=索引值<4的元素组成切片s;
- 得到的切片长度=high-low;
- 容量等于得到的切片的底层数组的容量。
func main() {
a := [5]int{1, 2, 3, 4, 5}
s := a[1:3] // s := a[low:high]
fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
//s:[2 3] len(s):2 cap(s):4
}
(三) 切片表达式注意
package main
import "fmt"
func main() {
a := [6]int{1, 2, 3, 4, 5, 6}
s := a[1:3] // s := a[low:high]
fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
//s:[2 3] len(s):2 cap(s):5
s2 := s[3:4] // 索引的上限是cap(s)而不是len(s)
fmt.Printf("s2:%v len(s2):%v cap(s2):%v\n", s2, len(s2), cap(s2))
//s2:[5] len(s2):1 cap(s2):2
}
(四) 要检查切片是否为空,请始终使用len(s) == 0来判断,而不应该使用s == nil来判断。因为只要初始化就不是nil,但是可能初始化一个空值,比如
func main() {
// 声明切片类型
var b = []int{} //声明一个整型切片并初始化
fmt.Println(b) //[]
fmt.Println(b == nil) //false
}
(五) 切片之间不能比较,切片只可以和nil比较
(六) 切片扩容源码,路径:C:\Program Files\Go\src\runtime\slice.go
newcap := oldCap
doublecap := newcap + newcap
if newLen > doublecap {
newcap = newLen
} else {
const threshold = 256
if oldCap < threshold {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < newLen {
// Transition from growing 2x for small slices
// to growing 1.25x for large slices. This formula
// gives a smooth-ish transition between the two.
newcap += (newcap + 3*threshold) / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = newLen
}
}
}
(七) 从切片中删除元素:a = append(a[:index], a[index+1:]...)
func main() {
// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
fmt.Println(a) //[30 31 33 34 35 36 37]
}
(八) append方法为切片追加元素
1. 通过var声明的零值切片可以在append()函数直接使用,无需初始化。
因为 append 函数内部会处理底层数组的扩容和数据的复制。当我们向一个未初始化的 slice 使用 append 函数时,Go 编译器会自动为我们进行初始化操作。
var s []int
s = append(s, 1, 2, 3)
s := []int{} // 没有必要初始化
s = append(s, 1, 2, 3)
var s = make([]int) // 没有必要初始化
s = append(s, 1, 2, 3)
2. 但是,要用索引赋值必须初始化
package main
func main() {
var c []int
c[0] = 1000 //错误,panic: runtime error: index out of range [0] with length 0
}