Go切片
摘要: 本文主要介绍了 Go 语言中的切片。包括切片与数组的区别,切片的定义、初始化方式(如直接初始化、从数组或切片获取、用 make 函数创建),切片长度和容量的概念及不一致的原因,切片内元素的修改、添加、删除的操作方法(如通过下标修改、用 append 函数添加和删除),切片的复制(值传递和引用传递,以及使用 copy 函数复制),还有通过修改字符串内容的方法。(由豆包进行生成)
切片和数组
- 切片: 保存一组特定数据类型的不定长的容器
- 数组: 保存一组特定数据类型的有定长的容器
切片的定义
切片的定义与数组的定义基本一直,只是用[]来声明。
package mai
import "fmt"
func main() {
// 声明一个长度为3 存储int类型数据 的数组
var arr1 [3]int
// 声明一个 存储string类型数据 的切片
var slice1 []string
fmt.Printf("%v - %T - %v\n", arr1, arr1, len(arr1))
fmt.Printf("%v - %T - %v\n", slice1, slice1, len(slice1))
fmt.Println(slice1 == nil) // nil 类似于 null
}
[0 0 0] - [3]int - 3 // [3]int 代表最大容量为3,存储数据类型为int的数组
[] - []string - 0 // []string 代表存储数据类型为string的切片
true
初始化切片
切片的初始化方式与数组基本一致。
3.1 切片的初始化
package main
import "fmt"
func main() {
arr1 := [3]int{1, 2, 3}
sliceA := []int{1, 2, 3}
fmt.Printf("%v - %T - %v \n", arr1, arr1, len(arr1))
fmt.Printf("%v - %T - %v \n", sliceA, sliceA, len(sliceA))
arr2 := [...]int{0: 5, 3: 4, 5: 6}
sliceB := []int{0: 5, 3: 4, 5: 6}
fmt.Printf("%v - %T - %v \n", arr2, arr2, len(arr2))
fmt.Printf("%v - %T - %v \n", sliceB, sliceB, len(sliceB))
}
[1 2 3] - [3]int - 3
[1 2 3] - []int - 3
[5 0 0 4 0 6] - [6]int - 6
[5 0 0 4 0 6] - []int - 6
切片除了通过上面这种方式进行初始化以外,还可以通过数组或者切片进行初始化
3.2 通过数组得到切片
package main
import "fmt"
func main() {
arr1 := [5]int{1, 2, 3, 4, 5}
sliceA := arr1[1:4]
fmt.Printf("%v - %T - %v \n", arr1, arr1, len(arr1))
fmt.Printf("%v - %T - %v \n", sliceA, sliceA, len(sliceA))
}
[1 2 3 4 5] - [5]int - 5
[2 3 4] - []int - 3
3.3 通过切片得到切片
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA[2:6]
fmt.Printf("%v - %T - %v \n", sliceA, sliceA, len(sliceA))
fmt.Printf("%v - %T - %v \n", sliceB, sliceB, len(sliceB))
}
[1 2 3 4 5 6 7 8 9 10] - []int - 10
[3 4 5 6] - []int - 4
3.4 使用make函数动态创建切片
package main
import "fmt"
func main() {
/*
make([]元素类型, 长度, 容量)
*/
sliceA := make([]int, 5, 10)
fmt.Println(sliceA)
}
[0 0 0 0 0]
切片的长度和容量
切片的长度可以通过len函数得到,而切片的容量则需要通过cap函数来得到,如果我们正常定义切片的话,其长度和容量应该是相等的。
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Printf("%v - %v", len(sliceA), cap(sliceA))
}
但是如果我们是通过数组或切片得到的切片,其长度和容量未必是相等的。
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA[2:6]
fmt.Printf("%v - %v", len(sliceB), cap(sliceB))
}
4 - 8
长度是4,容量却是8,很明显,是不一致的。
4.1 切片的长度和容量不一致的原因
- 长度: 切片的长度就是它所包含的元素个数
- 容量: 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数
单纯的看概念,可能难以理解,请看下面这张图
容量是指,从开始元素到末尾,假设我们截取从
2-4(对应下标为[1:4]),那么应该是这样的一个图。
此时长度应该为3,容量却为9
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA[1:4]
fmt.Printf("%v - %v", len(sliceB), cap(sliceB))
}
3 - 9
切片内元素的修改和元素的添加
元素的修改可以通过下标的方式来进行,而元素的添加则需要通过append函数来实现。
5.1 元素的修改
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceA[5] = 150
fmt.Println(sliceA)
}
[1 2 3 4 5 150 7 8 9 10]
5.2 使用下标来添加元素
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA[1:4]
fmt.Printf("%v - %v", len(sliceB), cap(sliceB))
}
以sliceB为例,我们知道,切片的长度是3,容量为9,按常理来说,我们应该是可以通过下标来添加元素的,但是实际上并非如此。
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA[1:4]
sliceB[3] = 150
fmt.Println(sliceB)
}
runtime error: index out of range [3] with length 3
5.3 使用append添加元素
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA[1:4]
fmt.Println(sliceB)
sliceB = append(sliceB, 5)
fmt.Println(sliceB)
// 如果要添加多个元素,用英文,进行分割
sliceB = append(sliceB, 6, 7, 8, 9, 10, 11)
fmt.Println(sliceB)
}
[2 3 4]
[2 3 4 5]
[2 3 4 5 6 7 8 9 10 11]
5.4 使用append添加切片
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := []int{11, 12, 13}
// 将sliceB中的元素添加到sliceA中,注意最后的三个...,这个不能省略
sliceA = append(sliceA, sliceB...)
fmt.Println(sliceA)
}
[1 2 3 4 5 6 7 8 9 10 11 12 13]
切片内元素的删除
在Go语言中,目前尚未提供从切片中删除元素的函数,但是我们可以通过append来实现这个功能。
package main
import "fmt"
func main() {
// 从sliceA中删除4和5
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceA = append(sliceA[:3], sliceA[5:]...)
fmt.Println(sliceA)
}
[1 2 3 6 7 8 9 10]
切片的复制
在Go中,切片的复制,使用的是引用传递,所以,如果我们通过=来复制某个切片时,当副本改变,切片的内容也会发生改变。
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := sliceA
sliceB[0] = 111
fmt.Println(sliceA)
fmt.Println(sliceB)
}
[111 2 3 4 5 6 7 8 9 10]
[111 2 3 4 5 6 7 8 9 10]
7.1 值传递和引用传递
在Go语言中,数组采用的是值传递,切片使用的是引用传递,接下来通过图解的方式来详细讲解值传递和引用传递
值传递
package main
import "fmt"
func main() {
arr := [3]int{1, 2, 3}
arr1 := arr
arr[0] = 111
fmt.Println(arr)
fmt.Println(arr1)
}
以当前代码为例,通过图解方式来详细分析值传递。
- 首先在内存中开辟一块空间用来存放{1, 2, 3}
- 然后将变量arr指向内存空间中的{1, 2, 3}
arr1 := arr会在内存中在开辟一块新的空间用来存放另一组{1,2,3}
- 然后再将变量arr1指向新的内存地址
- 此时由于两个变量指向了两个不同的内存地址,所以我们改变任意一个变量,都不会对其他变量产生影响
引用传递
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3}
sliceB := sliceA
sliceB[0] = 111
fmt.Println(sliceA)
fmt.Println(sliceB)
}
- 首先还是在内存中开辟一块空间,用来存放{1, 2, 3},并将变量
sliceA指向这块内存空间。
sliceB := sliceA时,Go并不会在单独创建一份内存,而是直接将sliceB也指向当前的内存空间。
- 此时,由于两个变量指向的是同一个内存地址,所以,当其中一个变量的数据发生改变时,另外一个也会发生改变。
7.2 使用copy复制切片
package main
import "fmt"
func main() {
sliceA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sliceB := make([]int, len(sliceA), len(sliceA)+1)
copy(sliceB, sliceA) // 将切片sliceA中的数据拷贝到sliceB中
fmt.Println(sliceA)
fmt.Println(sliceB)
sliceB[0] = 111
fmt.Println(sliceA)
fmt.Println(sliceB)
}
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5 6 7 8 9 10]
[111 2 3 4 5 6 7 8 9 10]
通过切片的方式修改字符串中的内容
package main
import "fmt"
func main() {
s := "你好,张先生"
// 如果是纯英文可以使用byte,包含中文则需要使用rune
SliceA := []rune(s) // 将字符串转为切片
SliceA[0] = '您'
s = string(SliceA)
fmt.Println(s)
}