Go 语言切片(Slice)
切片(slice)是 Go 语言中一种重要的数据类型,它是一种动态数组,比数组更灵活。与数组不同,切片的长度是可以改变的,允许高效地动态管理数据。切片通常用于操作和处理数据集合。
1. 切片的基本概念
切片是对数组的一个抽象,它提供了更灵活的操作方式。切片并不存储数据,而是引用底层数组的一段连续内存区域。切片的容量和长度是动态的,可以根据需要进行扩展。
1.1 切片的构成
切片包含三个字段:
- 指针(Pointer):指向底层数组的某个位置。
- 长度(Length):切片的元素数量。
- 容量(Capacity):从切片的开始位置到底层数组的末尾可用元素的数量。
2. 切片的创建
2.1 使用 make 创建切片
可以通过 make 函数创建切片,make 函数接受三个参数:切片的类型、切片的长度和切片的容量。
slice := make([]int, 5) // 长度为 5,容量为 5
fmt.Println(slice) // 输出:[0 0 0 0 0]
slice2 := make([]int, 5, 10) // 长度为 5,容量为 10
fmt.Println(slice2) // 输出:[0 0 0 0 0]
2.2 使用数组字面量创建切片
可以通过数组字面量直接创建一个切片,并初始化元素值。
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice) // 输出:[1 2 3 4 5]
2.3 从数组中创建切片
从一个数组中创建切片,通过指定一个切片范围。
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // 从索引 1 到 3 的元素(不包括索引 4)
fmt.Println(slice) // 输出:[2 3 4]
3. 切片的基本操作
3.1 访问和修改切片元素
可以使用索引访问和修改切片的元素。
slice := []int{1, 2, 3}
fmt.Println(slice[0]) // 输出:1
slice[1] = 20
fmt.Println(slice) // 输出:[1 20 3]
3.2 切片的长度和容量
- 长度(Length):使用
len()函数获取切片的长度。 - 容量(Capacity):使用
cap()函数获取切片的容量。
slice := []int{1, 2, 3, 4, 5}
fmt.Println(len(slice)) // 输出:5
fmt.Println(cap(slice)) // 输出:5
slice2 := make([]int, 3, 5)
fmt.Println(len(slice2)) // 输出:3
fmt.Println(cap(slice2)) // 输出:5
3.3 切片的追加操作(append)
使用 append 函数向切片末尾追加元素。如果切片的容量不足以容纳新元素,Go 会自动扩展切片的容量。
slice := []int{1, 2, 3}
slice = append(slice, 4, 5)
fmt.Println(slice) // 输出:[1 2 3 4 5]
slice = append(slice, []int{6, 7}...)
fmt.Println(slice) // 输出:[1 2 3 4 5 6 7]
append(slice, 4, 5)直接添加元素。append(slice, []int{6, 7}...)使用切片展开操作符...来追加另一个切片的元素。
3.4 切片的删除操作
Go 并没有内置的删除函数,但可以通过切片的组合来模拟删除操作。
slice := []int{1, 2, 3, 4, 5}
slice = append(slice[:2], slice[3:]...)
fmt.Println(slice) // 输出:[1 2 4 5]
通过切片的组合,将删除索引为 2 的元素(即 3)。
4. 切片的性能
切片的扩展是一个性能关键点。当切片的容量不够时,append 操作会导致切片重新分配内存,这可能会引发性能问题。Go 通过指数增长来扩展切片的容量,通常是将容量扩展为原来的 2 倍。
slice := make([]int, 0, 2)
for i := 0; i < 10; i++ {
slice = append(slice, i)
fmt.Printf("len: %d, cap: %d\n", len(slice), cap(slice))
}
输出会显示切片容量随着添加元素的增长情况。
5. 切片的引用特性
切片是引用类型,这意味着切片变量指向同一个底层数组。当对切片进行修改时,所有引用该底层数组的切片都会看到修改。
5.1 示例:修改切片会影响其他切片
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[1:4] // [2, 3, 4]
slice2 := arr[2:5] // [3, 4, 5]
slice1[0] = 99
fmt.Println(arr) // 输出:[1 99 3 4 5]
fmt.Println(slice2) // 输出:[99 3 4]
可以看到,修改 slice1 后,arr 和 slice2 中的相应元素也发生了变化。
6. 切片的扩容
当通过 append 操作使得切片的容量不够时,Go 会自动扩容。扩容时会分配一个新的更大的底层数组,并将原切片的元素复制到新的数组中。这时原切片的底层数组就不再与新切片共享内存。
7. 总结
- 切片 是 Go 语言中比数组更灵活的动态数据结构,可以动态增减长度。
- 切片通过
make函数创建,也可以通过字面量和数组创建。 - 切片的底层数据是数组,具有 长度 和 容量 属性,使用
len()和cap()函数分别访问。 - 切片是 引用类型,多个切片可以共享底层数组。
append()函数用于向切片末尾添加元素,并可能导致切片扩容。- 切片具有内存管理的优化,能够高效地操作数据集合。