go语言中数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。
go提供了一种更加灵活强悍的内置类型切片(“动态数组”)。
切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。
切片是一个引用类型,它的内部结构包含地址
、长度
和容量
。
一、切片的声明和初始化
1、直接声明切片
var name []T
其中:name-切片的名称
T-切片的类型
package main
import (
"fmt"
)
func main() {
var numSlice = []int{1, 3, 5, 7, 9}
fmt.Println(numSlice)
var stringSlice []string
stringSlice = []string{"北京", "上海", "深圳", "苏州"}
fmt.Println(stringSlice)
}
运行结果
[1 3 5 7 9]
[北京 上海 深圳 苏州]
2、基于数组创建切片
var slice = arr[low: high]
其中:表达式中的low和high表示一个索引范围(左包含,右不包含)
package main
import (
"fmt"
)
func main() {
var numArry = [5]int{1, 2, 3, 4, 5}
var numSlice = numArry[2:4]
fmt.Println(numSlice)
}
运行结果
[3 4]
3、使用make()函数构造切片
var slice = make([]T, len, cap)
其中:T-切片的元素类型
len-切片的长度
cap-切片的容量
package main
import (
"fmt"
)
func main() {
var intSlice = make([]int, 2, 4)
fmt.Println(intSlice)
fmt.Println(len(intSlice))
fmt.Println(cap(intSlice))
}
运行结果
[0 0]
2
4
二、切片的本质
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)
举例:
数组 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[0:5],相应示意图如下:
切片s2 := a[3:6],相应示意图如下:
对slice所做的任何修改都将反映在底层数组中。
当多个切片共享相同的底层数组时,每个切片对元素所做的更改将在数组中反映出来。
package main
import (
"fmt"
)
func main() {
arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("操作前:", arr)
slice := arr[:5]
fmt.Println("操作前:", slice)
slice[0] = 100
fmt.Println("操作后:", arr)
fmt.Println("操作后:", slice)
}
运行结果
操作前: [0 1 2 3 4 5 6 7]
操作前: [0 1 2 3 4]
操作后: [100 1 2 3 4 5 6 7]
操作后: [100 1 2 3 4]
三、切片的操作
1、切片的遍历
切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。
package main
import (
"fmt"
)
func main() {
slice := []int{0, 1, 2, 3, 4, 5, 6, 7}
for i := 0; i < len(slice); i++ {
fmt.Printf("[%v-%v] ", i, slice[i])
}
fmt.Println("")
for k, v := range slice {
fmt.Printf("[%v-%v] ", k, v)
}
}
运行结果
[0-0] [1-1] [2-2] [3-3] [4-4] [5-5] [6-6] [7-7]
[0-0] [1-1] [2-2] [3-3] [4-4] [5-5] [6-6] [7-7]
2、切片的复制拷贝
- 直接赋值拷贝,浅拷贝
拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容
package main
import (
"fmt"
)
func main() {
slice1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
slice2 := slice1
slice2[0] = 100
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
}
运行结果
slice1: [100 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4 5 6 7]
- 使用
copy()
函数操作,深拷贝
copy(destSlice, srcSlice)
其中:destSlice-目标切片
srcSlice-源切片
package main
import (
"fmt"
)
func main() {
slice1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
slice2 := make([]int, 5, 5)
copy(slice2, slice1)
fmt.Println("slice2:", slice2)
slice2[0] = 100
fmt.Println("slice1:", slice1)
fmt.Println("slice2:", slice2)
}
运行结果
slice2: [0 1 2 3 4]
slice1: [0 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4]
3、向切片中添加元素append
Go语言的内建函数append()可以为切片动态添加元素。 可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)
slice = append(slice, num1, num2)
slice = append(slice, slice...)
- append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。
- 但当slice中没有剩余空间(即(cap-len) == 0)时,切片就会自动按照一定的策略进行“扩容”,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。
package main
import (
"fmt"
)
func main() {
arr := [5]int{0, 1, 2, 3, 4}
slice := arr[:3]
slice = append(slice, 100)
fmt.Println("arr:", arr)
fmt.Println("slice:", slice)
fmt.Println("------------------")
slice = append(slice, 200)
fmt.Println("arr:", arr)
fmt.Println("slice:", slice)
fmt.Println("------------------")
slice = append(slice, 300)
fmt.Println("arr:", arr)
fmt.Println("slice:", slice)
}
运行结果
arr: [0 1 2 100 4]
slice: [0 1 2 100]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200 300]
4、从切片中删除元素
Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。
slice = append(slice[:index], slice[index+1:]...)
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
slice = append(slice[:3], slice[4:]...)
fmt.Println(slice)
}
运行结果
[1 2 3 5]
5、注意事项
- 切片不能进行比较,切片唯一合法的比较操作是和nil比较。
- 判断切片是否是空的,要使用
len(slice) == 0
来判断,不应该使用slice == nil
来判断
package main
import "fmt"
func main() {
var s1 []int
s2 := []int{}
s3 := make([]int, 0, 0)
fmt.Printf("s1: len = %v, cap = %v, isNil:%v
", len(s1), cap(s1), s1 == nil)
fmt.Printf("s2: len = %v, cap = %v, isNil:%v
", len(s2), cap(s2), s2 == nil)
fmt.Printf("s3: len = %v, cap = %v, isNil:%v
", len(s3), cap(s3), s3 == nil)
}
运行结果
s1: len = 0, cap = 0, isNil:true
s2: len = 0, cap = 0, isNil:false
s3: len = 0, cap = 0, isNil:false