1. 前言
python和golang中都有切片的用法,但效果却很有不同。
在python中列表属于可变对象, 而python的切片操作会返回一个新的列表,在切片上的操作不会对原有列表数据造成影响。
>>> a = [1,2,3]
>>> b = a
>>> c = a[:]
>>> b[0]=0
>>> a
[0, 2, 3]
>>> b
[0, 2, 3]
>>> c
[1, 2, 3]
>>> c[0]=4
>>> c
[4, 2, 3]
>>> a
[0, 2, 3]
>>> b
[0, 2, 3]
而在golang中恰恰相反, 数组是一个值类型, 当数组作为参数传递时, 实际传递的是一个副本, 而切片可以看做是对底层数组的引用。
2. golang 中的切片
在 Go 语言中,切片(slice)是一种灵活的、可变长的序列类型,它是基于数组(array)类型构建的。
切片是一个由元素组成的序列,每个元素可以是任意的类型。切片有三个重要的属性:
- 长度(Length):切片中元素的个数。
- 容量(Capacity):切片底层数组的大小,通常是从切片的起始位置到底层数组的末尾位置。
- 指针(Pointer):指向底层数组的第一个元素的指针。
切片的声明方式和数组很类似,只不过不需要指定长度,如下所示:
var s []int // 声明一个切片变量 s,元素类型为 int
var s = []int{1, 2} // 使用字面量声明一个切片
可以使用内置函数 make()
来创建一个指定类型的切片。make()
函数的第一个参数指定了切片的类型,第二个参数指定了切片的长度,第三个参数(可选)指定了切片的容量。例如:
s := make([]int, 5) // 长度为 5,容量为 5 的 int 类型切片
s := make([]int, 5, 10) // 长度为 5,容量为 10 的 int 类型切片
切片可以通过索引来访问其元素,也可以通过内置函数 append()
来动态添加元素。例如:
s := []int{1, 2, 3, 4, 5} // 创建一个 int 类型的切片
s[0] = 6 // 修改第一个元素为 6
s = append(s, 6, 7) // 添加两个元素 6 和 7 到切片 s 的末尾
切片的底层实现是数组,切片与数组的主要区别在于长度和容量。切片的长度可以在运行时动态改变,而数组的长度是固定的。因此,使用切片可以更加灵活地处理数据。
切片还有一个重要的特性是切片表达式(slice expression),可以用于截取切片中的一段序列。例如:
s := []int{1, 2, 3, 4, 5}
s1 := s[1:3] // 获取切片 s 中下标从 1 到 2 的元素,即 [2, 3]
在 Go 语言中,切片(slice)是一种灵活的、可变长的序列类型,它是基于数组(array)类型构建的。
切片是一个由元素组成的序列,每个元素可以是任意的类型。切片有三个重要的属性:
- 长度(Length):切片中元素的个数。
- 容量(Capacity):切片底层数组的大小,通常是从切片的起始位置到底层数组的末尾位置。
- 指针(Pointer):指向底层数组的第一个元素的指针。
切片的声明方式和数组很类似,只不过不需要指定长度,如下所示:
var s []int // 声明一个切片变量 s,元素类型为 int
var s = []int{1, 2} // 使用字面量声明一个切片
可以使用内置函数 make()
来创建一个指定类型的切片。make()
函数的第一个参数指定了切片的类型,第二个参数指定了切片的长度,第三个参数(可选)指定了切片的容量。例如:
s := make([]int, 5) // 长度为 5,容量为 5 的 int 类型切片
s := make([]int, 5, 10) // 长度为 5,容量为 10 的 int 类型切片
切片可以通过索引来访问其元素,也可以通过内置函数 append()
来动态添加元素。例如:
s := []int{1, 2, 3, 4, 5} // 创建一个 int 类型的切片
s[0] = 6 // 修改第一个元素为 6
s = append(s, 6, 7) // 添加两个元素 6 和 7 到切片 s 的末尾
切片的底层实现是数组,切片与数组的主要区别在于长度和容量。切片的长度可以在运行时动态改变,而数组的长度是固定的。因此,使用切片可以更加灵活地处理数据。
切片还有一个重要的特性是切片表达式(slice expression),可以用于截取切片中的一段序列。例如:
s := []int{1, 2, 3, 4, 5}
s1 := s[1:3] // 获取切片 s 中下标从 1 到 2 的元素,即 [2, 3]
切片表达式包含两个参数,第一个参数表示起始位置,第二个参数表示结束位置(不包含该位置的元素)。如果省略第一个参数,则默认为 0;如果省略第二个参数,则默认为切片的长度。
3. golang 中的数组与切片
在 Go 语言中,数组和切片都可以用来存储一组值,但它们在很多方面是不同的。
-
首先,数组的长度是固定的,一旦定义了长度,就不能再改变。而切片的长度可以动态扩容和缩容,因此可以更灵活地处理数据。
-
其次,数组是一个值类型,当将一个数组传递给函数时,实际上传递的是该数组的一个副本。因此,如果在函数中修改了数组的元素,原始数组不会受到影响。而切片是一个引用类型,当将一个切片传递给函数时,实际上传递的是该切片底层数组的地址。因此,如果在函数中修改了切片中的元素,原始切片和其他引用该底层数组的切片都会受到影响。
-
最后,数组的定义方式为
[n]T
,其中n
表示数组的长度,T
表示数组中元素的类型。而切片的定义方式为[]T
,其中T
表示切片中元素的类型。因此,如果将一个数组作为参数传递给函数,实际上传递的是该数组的副本,而不是数组本身。如果要传递数组的地址,需要将数组转换为切片类型,然后将切片传递给函数。例如:
func printS(s []int) {
fmt.Println(s) // 输出[1 2 3]
fmt.Println("长度:", len(s), "容量:", cap(s)) // 两个值都是3
}
func main() {
arr := [3]int{1, 2, 3} // 创建一个 int 类型的切片
// printS(arr) // 报错
s := arr[:]
printS(s)
s[0] = 0
fmt.Println("arr[0]", arr[0]) // 输出0
}
4. 切片的拷贝
在有些场景中,我们并不希望切片中的内容被修改,可以借助内置函数 copy()
来拷贝切片。
在 Go 中,可以使用内置函数 copy()
来拷贝切片。
copy()
函数的语法为:
copy(destSlice []T, srcSlice []T) int
其中,destSlice
是目标切片,srcSlice
是源切片,T
是切片的元素类型,函数返回值为拷贝的元素个数。
copy()
函数会将源切片的元素复制到目标切片中,如果源切片的长度小于目标切片的长度,则只会拷贝源切片的部分元素;如果源切片的长度大于目标切片的长度,则只会拷贝目标切片的部分元素。如果目标切片的长度小于源切片的长度,那么目标切片中剩余的元素会保持不变。
以下是一个使用 copy()
函数拷贝切片的例子:
func main() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := make([]int, len(slice1))
slice3 := make([]int, 3)
slice4 := make([]int, 10)
var slice5 []int
// 将 slice1 的元素拷贝到 slice2 中
n2 := copy(slice2, slice1)
n3 := copy(slice3, slice1)
n4 := copy(slice4, slice1)
n5 := copy(slice5, slice1)
slice2[0] = 0
fmt.Println("slice1:", "长度:", len(slice1), "容量:", cap(slice1), slice1)
fmt.Println("slice2:", "拷贝长度", n2, "长度:", len(slice2), "容量:", cap(slice2), slice2)
fmt.Println("slice3:", "拷贝长度", n3, "长度:", len(slice3), "容量:", cap(slice3), slice3)
fmt.Println("slice4:", "拷贝长度", n4, "长度:", len(slice4), "容量:", cap(slice4), slice4)
fmt.Println("slice5:", "拷贝长度", n5, "长度:", len(slice5), "容量:", cap(slice5), slice5)
}
运行以上代码会输出:
slice1: 长度: 5 容量: 5 [1 2 3 4 5]
slice2: 拷贝长度 5 长度: 5 容量: 5 [0 2 3 4 5]
slice3: 拷贝长度 3 长度: 3 容量: 3 [1 2 3]
slice4: 拷贝长度 5 长度: 10 容量: 10 [1 2 3 4 5 0 0 0 0 0]
slice5: 拷贝长度 0 长度: 0 容量: 0 []xxxxxxxxxx slice1: 长度: 5 容量: 5 [1 2 3 4 5]slice2: 拷贝长度 5 长度: 5 容量: 5 [0 2 3 4 5]slice3: 拷贝长度 3 长度: 3 容量: 3 [1 2 3]slice4: 拷贝长度 5 长度: 10 容量: 10 [1 2 3 4 5 0 0 0 0 0]slice5: 拷贝长度 0 长度: 0 容量: 0 []slice1: [1 2 3 4 5]slice2: [1 2 3 4 5]