在定义数组时,我们不但给出了数据类型,还规定了数组的大小。这意味着一旦创建了数组,就不能改变它的大小。不过某些情况下,固定长度的数组可能会带来不便。
Go 通过使用切片支持可变大小的数组结构。与数组不同,切片数据类型不依赖其长度,因此切片是一个动态而强大的工具,比数组更灵活。本质上,切片是数组中值的子集。
1. 切片的工作原理
切片是建立在数组类型之上的抽象。这意味着它允许使用与数组相同的逻辑,但具有更强大的功能和灵活性。
切片是数组的一部分;也就是说,可以创建一个数组,然后再创建一个切片,切片只引用该数组中的一些元素。由于数组中的每个元素必须是相同的数据类型,因此切片中的每个元素也必须是相同的数据类型。
切片包括指向数组中的元素的指针、切片的长度以及它的容量(切片能够容纳的最大元素数量,这取决于底层数组的大小)。
2. 对数组进行切片
为更好地理解切片和数组之间的关系,可以看一个例子。在这个例子中,首先创建一个包含 7 个整数的数组,然后创建该数组的一个切片。代码如下所示,它通过使用原始数组位置 0~4 的值来创建一个切片。
package main
import (
"fmt"
"reflect"
)
func main() {
numbers := [7]int{0, 1, 2, 5, 798, 43, 78}
fmt.Println("array value:", numbers)
fmt.Println("array type:", reflect.TypeOf(numbers))
s := numbers[0:4]
fmt.Println("slice value:", s)
fmt.Println("slice type:", reflect.TypeOf(s))
}
这个代码清单首先定义了一个名为 numbers 的数组(它包含7个 int 类型的元素),并在声明时进行了赋值。然后打印出这个数组,接着使用 reflect 包中的 TypeOf 方法来证明 number 确实是一个数组。
array value: [0 1 2 5 798 43 78]
array type: [7]int
下面的代码将 s 声明为 numbers 数组的一个切片。
s := numbers[0:4]
该切片包括指针 0 (引用数组中的第一个元素)和接下来的 3个元素。定义切片后,再使用两条打印语句。这一次打印的是 s 的值和类型。
slice value: [0 1 2 5]
slice type: []int
注意,在输出中数组类型显式包含数组的大小 (7),而切片类型则不包含大小。上述代码清单的完整输出如下:
array value: [0 1 2 5 798 43 78]
array type: [7]int
slice value: [0 1 2 5]
slice type: []int
2.1 使用 len 和 cap
要计算切片的长度和容量,可以使用 len 和 cap 函数。下面的代码演示了如何使用这个函数来确定切片的长度和容量。
package main
import (
"fmt"
)
func main() {
numbers := [7]int{0, 1, 2, 5, 798, 43, 78}
fmt.Println(numbers)
mySlice := numbers[1:5]
fmt.Println(mySlice)
fmt.Println("Length of slice:", len(mySlice))
fmt.Println("Slice capacity:", cap(mySlice))
}
在本例中,创建了一个名为 mySlice 的切片,它从数组的第二个元素开始,包含从该点开始的 4 个元素。
- 指针为 1,因为它是原始数组中包含的第二个元素
- 长度是 4,因为包含了 4 个元素。从数组的索引位置 1 开始,到索引位置 5。
- 切片的容量是由数组本身的大小决定的。在这个例子中,由于从索引位置 1 开始切片,因此排除了数组中的第一个元素。因为该数组包含 7 个元素,所以该切片的容量为 6 。
上述代码清单的输出结果如下所示:
[0 1 2 5 798 43 78]
[1 2 5 798]
Length of slice: 4
Slice capacity: 6
2.2 使用快捷方式
可以使用快捷符号来定义切片的大小和元素,这样可以更简洁。基本的语法包括指针和长度,同时 Go 语言也会为它们提供默认值。
3. 改变切片的大小
由于切片不像数组那样由其大小定义,因此可以从原始数组创建不同长度的切片。在下面代码中,使用 numbers 数组中的 4 个元素创建了一个名为 s 的切片。然后,将该切片替换为长度与原始切片容量相同的切片。
4. 对切片进行迭代
可以使用 for 循环来迭代切片中的元素。下面的代码创建一个数组,然后创建一个基于该数组的切片。最后使用 for 循环来打印切片的值。
5. make 函数
切片总是依赖数组,因此切片中的每个元素必须具有相同的数据类型。另外,虽然在声明时可以定义切片的初始大小,但切片的大小并非其定义的固有部分。
两个或多个切片可以引用相同的数组。如果将一个切片分配给另一个切片,那么这两个切片将引用同一个数组,即便没有明确指出引用自哪个数组。
Go 语言还允许使用内置的 make 函数直接创建切片,而无须先创建一个数组。当使用 make 函数时,编译器会在内部创建一个数组,然后创建一个引用该数组的切片,如下面代码所示:
6. 使用 var 创建切片变量
我们也可以使用 var 来创建切片变量,语法如下:
var variableName []dataType
在下面的代码清单中,创建了一个空的切片变量,然后显示了该切片及其类型。这可以使用 var 来实现。
7. 处理切片元素
切片虽然显式或隐式地依赖数组,但其中每个元素都可以使用基于该切片的索引值进行标识,其中第一个元素的索引值为 0 ,而不管该元素在原始数组中的索引值为何。
7.1 替换切片中的元素
通过该问切片中的单个元素,可以对它们进行更改。这包括替换切片中单个元素的值,如下面代码清单所示:
7.2 使用空切片
值得一提的是,如果切片为空,则无法使用索引向其中添加元素。
7.3 使用切片的部分元素
7.4 在切片中使用 range
8. 使用 append 函数向切片追加值
虽然不能将值追加到数组,但是可以将值追加到切片中。使用 append 函数可以在切片的未尾添加一个新元素。如果切片是空的,新元素将被放置在索引 0 处。如果切片不为空,append 会将元素添加到第一个可用位置。下面代码清单演示了如何创建一个名为 s 的空切片,然后向其中添加两个元素。
9. 复制切片
可以使用 copy 函数将一个切片的内容复制到另一个切片中。在下面的代码中,创建了一个包含两个元素的切片和一个空切片。然后将第一个切片中的元素复制到第二个切片中。
10.使用 new 关键字创建切片
我们可以使用 new 关键字创建切片。这种情况下,可以使用如下语法:
var newSlice = new ([capacity] type) (startingElement:length)
11.从切片中删除元素
Go 中没有一个内置函数允许从切片中轻松删除元素,但你可以使用重新切片的概念。也就是说,从原始切片构建一个新切片,同时删除不想要或不需要的元素。
让我们首先考虑下面的函数 RemoveIndex 。该函数接收一个 inSlice 切片 和一个索引(要删除元素的索引)作为输入,并返回一个新切片。
func RemoveIndex(inSlice []int, index int) []int {
return append(inSlice[:index],inSlice[index+1:]...)
}
在这个函数中,只需要使用 append 函数来连接两个切片。第一个切片是 inSlice[:index],它将包含要删除的元素之前的所有元素。第二个切片是 inSlice[index+1:],它包含要删除的元素之后的所有元素。然后通过 append 函数将这两个切片连接起来,并将连接后的结果作为函数的返回值。