Go 切片(Slice)
切片是一种动态数组,声明长度默认为 0 的数组即为一个切片。
var stringSlice []string
创建切片
使用切片字面量
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(numbers) // 输出: [1 2 3 4 5]
使用 make
函数
使用 make
函数创建切片,可以指定长度和容量。
numbers := make([]int, 3, 5)
fmt.Println(numbers) // 输出: [0 0 0]
从数组创建切片
array := [5]int{1, 2, 3, 4, 5}
slice := array[1:4]
fmt.Println(slice) // 输出: [2 3 4]
添加元素
使用 append
函数可以向切片添加元素。
var names []string
names = append(names, "Alice", "Bob", "Charlie")
fmt.Println(names) // 输出: [Alice Bob Charlie]
注意:
append
返回一个新的切片,需要用变量接收。- 新的切片必须赋值给原切片,才能实现增加元素的效果。
append
返回的是一个新的引用。
展开操作
可以使用 ...
将一个切片的元素展开,并添加到另一个切片中。
func main() {
students := []string{"John", "Paul", "George", "Ringo"}
moreStudents := []string{"Mick", "Keith", "Charlie", "Ronnie"}
students = append(students, moreStudents...)
for index, name := range students {
fmt.Println(index, name)
}
}
复制切片
使用 copy
函数可以复制切片。
sourceSlice := []int{1, 2, 3}
destinationSlice := make([]int, len(sourceSlice))
copy(destinationSlice, sourceSlice)
fmt.Println(destinationSlice) // 输出: [1 2 3]
注意:
copy
不会扩充目标切片的长度,目标切片必须有足够的长度。copy
返回被复制的元素数量。
删除元素
要从切片中删除元素,可以使用切片的截取和 append
。
numbers := []int{1, 2, 3, 4, 5}
// 删除索引为 2 的元素
numbers = append(numbers[:2], numbers[3:]...)
fmt.Println(numbers) // 输出: [1 2 4 5]
访问和修改元素
numbers := []int{1, 2, 3, 4, 5}
// 访问索引为 2 的元素
fmt.Println(numbers[2]) // 输出: 3
// 修改索引为 2 的元素
numbers[2] = 10
fmt.Println(numbers) // 输出: [1 2 10 4 5]
获取切片的长度和容量
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(len(numbers)) // 长度: 5
fmt.Println(cap(numbers)) // 容量: 5
截取切片
切片的截取操作通过更新切片的指针、长度和容量来完成。
array := []string{"a", "b", "c", "d", "e", "f", "g", "h"}
// 截取索引从 1 到 5 的元素(不包含索引 5)
subSlice := array[1:5]
fmt.Println(subSlice) // 输出: [b c d e]
截取到末尾
subSlice := array[1:]
fmt.Println(subSlice) // 输出: [b c d e f g h]
从起始位置截取
subSlice := array[:4]
fmt.Println(subSlice) // 输出: [a b c d]
截取整个切片
subSlice := array[:]
fmt.Println(subSlice) // 输出: [a b c d e f g h]
注意:新的切片变量指向同一个底层数组。
array := []string{"a", "b", "c", "d", "e"}
subSlice := array[:3]
array[0] = "z"
fmt.Println(subSlice) // 输出: [z b c]
fmt.Println(array) // 输出: [z b c d e]
当修改底层数组的元素时,所有引用该数组的切片都会受影响。
遍历切片
可以使用 for
循环或 for range
语句遍历切片。
students := []string{"John", "Paul", "George", "Ringo"}
// 使用传统的 for 循环
for i := 0; i < len(students); i++ {
fmt.Println(i, students[i])
}
// 使用 for range 循环
for index, name := range students {
fmt.Println(index, name)
}
切片的容量扩展
当使用 append
向切片添加元素,且超过其容量时,Go 会自动分配更大的底层数组。
numbers := []int{1, 2, 3}
fmt.Println(len(numbers), cap(numbers)) // 输出: 3 3
numbers = append(numbers, 4)
fmt.Println(len(numbers), cap(numbers)) // 输出: 4 6
切片的容量增长策略是指数型增长,以减少内存分配的次数。
切片的内存共享
由于切片共享底层数组,在某些情况下可能需要避免内存共享带来的副作用。
func main() {
data := []int{1, 2, 3, 4, 5}
subData := data[2:4] // [3 4]
subData[0] = 100
fmt.Println(data) // 输出: [1 2 100 4 5]
fmt.Println(subData) // 输出: [100 4]
}
如果想要创建切片的副本,可以使用 copy
函数。
func main() {
data := []int{1, 2, 3, 4, 5}
subData := make([]int, 2)
copy(subData, data[2:4]) // 复制 [3 4] 到 subData
subData[0] = 100
fmt.Println(data) // 输出: [1 2 3 4 5]
fmt.Println(subData) // 输出: [100 4]
}
切片与数组的区别
- 数组是定长的,长度是类型的一部分,长度固定后不能改变。
- 切片是变长的,可以动态增加或减少元素,长度可变。
- 切片是对底层数组的一个引用,操作切片会影响底层数组。
最佳实践
- 在函数参数中,尽量使用切片而非数组,以获得更大的灵活性。
- 当需要避免底层数组的副作用时,可以使用
copy
创建切片的副本。 - 遍历切片时,使用
for range
循环更加简洁。