引入
数组长度固定,实际开发中数据量经常变化,Go 提供了切片来解决这个问题。切片是动态长度的序列,底层基于数组实现,比数组灵活得多,是 Go 里最常用的集合类型。
正文
定义
定义
切片是动态长度的序列,比数组更常用。切片本身不存储数据,而是引用一段底层数组。切片有三个属性:指针(指向底层数组的起始位置)、长度(
len)和容量(cap)。
语法
// 字面量创建
sl := []int{1, 2, 3}
// make 创建(指定长度和容量)
sl := make([]int, 5, 10)
// 从数组或切片切取
sl := arr[low:high]例子
字面量创建
sl := []int{10, 20, 30}
fmt.Println(sl) // [10 20 30]
fmt.Println(len(sl)) // 3
fmt.Println(cap(sl)) // 3字面量创建时长度和容量相等。
make() 创建
sl := make([]int, 3, 5)
fmt.Println(sl) // [0 0 0]
fmt.Println(len(sl)) // 3
fmt.Println(cap(sl)) // 5make([]T, len, cap) 创建一个长度 len、容量 cap 的切片,元素为零值。容量省略时等于长度。
从数组切取
arr := [5]int{10, 20, 30, 40, 50}
sl := arr[1:4]
fmt.Println(sl) // [20 30 40]切片和原数组共享底层数组,修改切片会影响数组。
append() 追加元素
sl := []int{1, 2, 3}
sl = append(sl, 4)
fmt.Println(sl) // [1 2 3 4]
sl = append(sl, 5, 6)
fmt.Println(sl) // [1 2 3 4 5 6]append() 返回新切片,必须用变量接收。容量不够时会自动扩容。
append() 追加另一个切片
a := []int{1, 2}
b := []int{3, 4}
a = append(a, b...)
fmt.Println(a) // [1 2 3 4]用 … 展开切片,把 b 的元素逐个追加到 a。
copy() 复制切片
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
dst[0] = 100
fmt.Println(src) // [1 2 3](没变)
fmt.Println(dst) // [100 2 3]copy() 把数据复制到新切片,两者互不影响。
切片的切片
sl := []int{1, 2, 3, 4, 5}
sub := sl[1:3]
fmt.Println(sub) // [2 3]
fmt.Println(len(sub)) // 2
fmt.Println(cap(sub)) // 4(到底层数组末尾)子切片的容量从切取位置到底层数组末尾,不是子切片的长度。
扩容规则
sl := make([]int, 0, 4)
sl = append(sl, 1, 2, 3, 4)
fmt.Println(cap(sl)) // 4
sl = append(sl, 5)
fmt.Println(cap(sl)) // 8(容量翻倍)容量不足时,Go 自动分配更大的底层数组并复制数据。容量 < 256 时翻倍,>= 256 时增长 25%。
删除元素
sl := []int{1, 2, 3, 4, 5}
// 删除索引 2 的元素
sl = append(sl[:2], sl[3:]...)
fmt.Println(sl) // [1 2 4 5]Go 没有内置 delete 用于切片,用 append 拼接前后两段来删除。
nil 切片和空切片
var s1 []int // nil 切片
s2 := []int{} // 空切片
fmt.Println(s1 == nil) // true
fmt.Println(s2 == nil) // falsenil 切片和空切片都可以 append(),行为一致。
常见写法
| 写法 | 说明 |
|---|---|
sl := []int{1, 2, 3} | 字面量创建 |
sl := make([]int, len, cap) | make() 创建 |
sl := arr[1:4] | 从数组/切片切取 |
sl = append(sl, v) | 追加元素 |
sl = append(sl, b…) | 追加另一个切片 |
copy(dst, src) | 复制切片 |
sl[:i] | 取前 i 个元素 |
sl[i:] | 从第 i 个到末尾 |
特点
- 切片是引用类型,底层指向数组,赋值和传参共享数据
len()是当前长度,cap()是底层数组容量append()容量不够时自动扩容,返回新切片copy()创建独立副本,修改互不影响- 切片切取后和原数组共享底层数组,修改会互相影响
- 删除元素没有内置函数,用
append(sl[:i], sl[i+1:]…)实现 nil切片可以直接append(),不会 panic
理解
切片就是一个”窗口”,透过它看到的是一段底层数组。append() 会让窗口自动变大,copy() 会复制一份独立的数据。日常写 Go 几乎不用数组,全用切片。记住切片和底层数组的关系,就能避免大部分共享数据的坑。
引出
切片是按索引存取数据,如果想用自定义的键来查找数据,接下来看 Go Map,学习用键值对保存数据的集合类型。