文章目录
1、切片概念
1.1、基本介绍
切片是对底层数组的连续片段的引用,使得切片成为了引用类型。它的长度可以在运行时改变,其长度永远不会超过其容量,即 0 <= len(s) <= cap(s)
。切片的灵活性在于,你可以使用切片来访问数组的任何连续部分,而无需复制其元素。
1.2、创建与初始化
切片可以通过简单的切片表达式来创建:
var slice1 []type = arr1[start:end]
这样 slice1
将引用 arr1
中从 start
到 end-1
的部分。如果省略 start
或 end
,默认分别为 0 和数组的长度。例如,arr1[:]
创建一个完整的数组切片。
你还可以直接初始化切片:
s := []int{1, 2, 3}
1.3、切片操作
切片支持多种操作,包括重新切片和索引访问。重新切片可以扩展或缩减切片的长度,但不能超过其容量。如果需要超过容量的切片,可以使用内置的 append
函数,它可以动态地增加切片的容量。
s := []int{1, 2, 3}
s = append(s, 4) // 追加元素到切片
2、使用切片
2.1、传递切片到函数
由于切片是引用类型,将切片传递到函数中非常高效,不涉及数据复制。例如,计算切片所有元素的和:
func sum(s []int) int {
total := 0
for _, v := range s {
total += v
}
return total
}
2.2、切片的内存组织
在内存中,切片由三部分组成:指向底层数组的指针、切片的长度和容量。这种结构使得切片操作高效且灵活。
2.3、创建切片
除了基于数组的切片创建外,可以使用 make
函数直接创建切片:
s := make([]int, 10, 20)
这里 s
是一个长度为 10,容量为 20 的切片。这种方式允许我们在未定义数组的情况下直接操作切片。
3、切片与数组的区别
切片和数组在 Go 中都用于存储序列数据,但它们的设计目的和使用方式有所不同。
3.1、数组
数组是具有固定大小的数据结构,定义时需要指定元素数量,这个数量在数组生命周期内不能改变。数组的长度是其类型的一部分,例如 [5]int
和 [10]int
是不同的类型。数组的这种固定大小的特性意味着在数组创建时,内存就被分配且大小不会改变。
3.2、切片
切片则提供了更大的灵活性和功能:
- 动态大小:切片的长度可在运行时改变,提供了类似动态数组的功能。
- 容量和长度:切片有两个属性,长度(当前元素数量)和容量(底层数组可以容纳的元素数量)。切片可以通过
append
函数动态扩展,当容量不足以容纳更多元素时,Go 会自动扩展容量。 - 基于引用:切片通过引用底层数组来存储元素,这意味着多个切片可以共享同一个数组的部分或全部,修改一个切片的元素可能会影响共享同一数组的其他切片。
因此,当需要一个容量可变的数据集时,切片是一个比数组更合适的选择。切片的这些特性使得它在处理不确定大小的数据集时特别有用。
4、多维切片
与数组不同,切片可以很方便地创建和操作多维数据结构,因为每一个维度都可以独立地扩展和收缩。
多维切片通常通过切片的切片来实现,例如 [][]int
表示一个二维整数切片。每一个内层切片可以独立地进行增长或收缩,这为处理如不规则表格数据提供了极大的灵活性。
例如,初始化一个动态的二维切片并赋值:
rows := 2
cols := 3
matrix := make([][]int, rows)
for i := range matrix {
matrix[i] = make([]int, cols)
for j := range matrix[i] {
matrix[i][j] = i + j
}
}
这种灵活性使得切片成为处理复杂数据结构的理想选择,特别是当数据结构的维度在运行时可能变化时。
5、For-range 结构
for-range
结构提供了一种简洁的方式来迭代数组和切片中的每个元素。这种结构自动处理索引和元素的提取,使得代码更清晰、更易于维护。
当使用 for-range
循环遍历切片或数组时,每次迭代会返回两个值:索引和该索引位置的元素的副本。如果你只需要元素值,可以忽略索引:
slice := []int{1, 2, 3}
for _, value := range slice {
fmt.Println(value)
}
如果你需要修改元素的值,应该使用索引直接
访问元素:
for i := range slice {
slice[i] *= 2
}
这种结构也适用于多维切片,每一层迭代可以处理一个维度。
通过结合切片的灵活性和 for-range
结构的易用性,Go 程序员可以有效地处理各种复杂和动态的数据集。