Go语言切片
上一小节介绍了Go语言的数组,数组是固定大小的具有相同类型的序列,在内存中,就是一块连续的内存
但是由于数组是固定大小的,使用上有些限制,所以在Go语言中,数组使用的不是很频繁
Go语言提供了一个类似Java
语言中的 List
,类似 C++ 中的 vector
,类似 Python中的列表[]
这种数据结构就是切片
,英文是 slice
Go语言中的切片就是动态数组
,没有固定大小,可以删除,追加元素,原理和 Java
语言中的 ArrayList
一样
1 定义切片
切片切片使用[]dataType
定义,其中 []
是必须的,dataType
代表的是切片中元素的数据类型。
Go语言切片的定义有 2 种方式,
先看第一种:var slice []类型
1.1 直接通过 var 声明
语法如下:
var 名字 []类型
,比如var s []int
示例如下:
//定义了一个切片 s , 里面的元素类型是 int var s []int fmt.Println(s) //输出: []
上面声明了一个切片s
,元素类型都是int
打印出来的是个空,这种方式声明的切片,默认值是 nil
,长度为 0
可以看下面的例子,验证一下:
func main() { var slice []int if slice == nil { fmt.Println("slice == nil ") } } //输出: slice == nil
1.2 通过 make 声明切片
通过make
函数定义切片
语法为:make([]类型,len, cap)
其中
-
类型
就是里面元素的数据类型 -
len
是必填参数,代表切片的大小 -
cap
是可选参数,代表切片的容量,如果不填,默认等于len
,如果填写,必须满足cap >= len
示例如下:
func main() { s := make([]int,5) fmt.Println(s) //输出: [0 0 0 0 0] }
从上面可知:
-
通过
make
函数定义了一个len = 5
, 类型为int
的切片s
-
此时
s
不为nil
了 ,因为打印出来的是有 5 个元素的切片 -
切片中每个元素的默认值是相应类型的默认值, int 为 0 , string 为 ""
2 切片和数组的区别
切片
是“动态数组”,没有固定的大小,可以往里面追加元素,随着元素的增多,底层会自动扩容
数组
是有固定的大小,里面能存放多少元素是一定的
很多人会把 切片和数组的定义弄混淆分不清楚。这里面有一个很好的区分方法。
我们先回顾一下数组和切片的定义,然后再讲区分方法
或者切片
2.1 数组的定义
分别定义一个 元素的类型为int
的,大小为 5 的数组
如下 4 种方法:
-
var arr = [5]int
-
var arr = [...]{1,2,3,4,5}
-
arr := [5]int {1,2,3,4,5}
-
arr := [...]int {1,2,3,4,5}
2.2 切片的定义
和上面一样:分别定义一个 元素的类型为int
的,len 为 5 的切片
如下 2 种方法:
-
var s []int
-
s := make([]int,5)
通过上面 数组
和 切片
的定义可以知道,他们的区别就在于 [ ]
里面是否有 "东西"
对于数组:不管哪种定义方式,[ ]
里面要么是一个数
,比如 var arr = [5]int
,要么是 ...
, 比如var arr = [...]{1,2,3,4,5}
总之,数组的定义,[ ]
里面必须有个 "东西" , 数
或者 ...
对于切片: 不管哪种定义方式,[ ]
里面没有 "东西" ,比如var s []int
或者 s := make([]int,5)
, [ ]
里面啥也没有
所以,切片 和 数组 定义的区别在于: [ ]
中是否有东西,有东西的是数组,没有东西的是切片
3 切片截取
切片支持索引读取,通过索引截取,可以从 数组上面截取,也可以从切片上截取,返回的结果还是一个切片
3.1 切片的读取
像数组一样,通过索引对切片进行赋值,读取。 索引从 0 开始
示例如下:
func main() { s := make([]int,5) fmt.Println(s) //输出: [0 0 0 0 0] s[1] = 13 fmt.Println(s) //输出:[0 13 0 0 0] }
3.2 切片的截取
切片可以从数组,切片中,截取一部分,成为一个新的切片
语法为:newSlice := s[ start : end ]
其中:
- newSlice 是截取后的切片
- s是切片或者数组
- start , end 是索引,前包括,后不包括,即前开后闭
- 中间使用冒号
:
分隔
如下示例:
func main() { arr := [7]int{ 0 , 1, 2, 3, 4, 5, 6} s := arr[1:5] fmt.Println( s ) //输出:[1 2 3 4] //从索引 2 一直到最后 s1 := arr[2:] fmt.Println(s1) //输出:[2 3 4 5 6] //从开始的 0 到索引 4 (不包括 4 ) s2 := arr[:4] fmt.Println( s2 ) //输出:[0 1 2 3] //从 0 到最后,等于把整个数组截取了 s3 := arr[:] fmt.Println( s3 ) //输出:[0 1 2 3 4 5 6] //从切片 s 上截取 s4 := s[1:3] fmt.Println(s4) //输出:[2 3] }
3.3 切片的添加
向切片中添加元素,使用内置的 append()
函数
由于 append()
函数返回的是一个新的 切片,所以需要再接收一下
看下面的例子就明白了:
func main() { var s []int // 必须再重新赋值给 s // 否则会报错 s = append(s, 1) s = append(s, 2) s = append(s, 3) fmt.Println(s) } //输出:[1 2 3]
从上面可知:
-
append
返回一个新的切片,必须再用s
接收,否则s
里面不会添加元素,如下: s 打印出来还是 [ ]func main() { var s []int s2 := append(s, 1) fmt.Println("s=" ,s) fmt.Println("s2=",s2) } //输出如下: s= [] s2= [1]
-
var s []int
声明的切片是 nil , 但是在Go语言里面,是可以添加元素直接使用的,并不会报错
3.4 切片的长度和容量
Go语言中,切片是“动态的数组”,在切片被定义时,就已经为切片分配了一个一定长度的数组。
当切片添加的元素超过了底层的数组的大小时,Go语言会再分配一个更大的数组给切片引用。
所以切片的长度和容量分别是:
-
长度
就是切片中的元素的数量,使用内置的len()
函数获取切片长度 -
容量
就是切片底层引用的数组的大小,使用内置的cap()
函数获取切片的容量 -
容量
大于等于长度
,即cap >= len
-
定义切片时,如果不指定容量,则默认
cap == len
示例如下:
func main() { var s = []int {1,2,3} fmt.Println( "长度=",len(s), "容量=",cap(s) ) //输出:长度= 3 容量= 3 s = append(s, 4) fmt.Println( "长度=",len(s), "容量=",cap(s) ) //输出:长度= 4 容量= 6 s = append(s, 4,5,6) fmt.Println( "长度=",len(s), "容量=",cap(s) ) //输出:长度= 7 容量= 12 } //输出如下: 长度= 3 容量= 3 长度= 4 容量= 6 长度= 7 容量= 12
从上面可以看到,随着元素的添加,超过切片的容量之后,切片引用的底层数组的大小变为了原来的 2 倍
Go语言切片总结
通过上面的介绍,Go语言的切片是一个动态的可增长的数据结构,和Java中的 ArrayList 类似,和C++中的 vector 类似
现总结如下:
-
Go语言切片定义有 2 种方式
- 通过
var
方式,比如var s []int
- 通过
make
函数方式,比如make([]int,5)
, 第2个参数为长度,第3个参数可以省略,省略默认和长度相等
- 通过
-
通过
var s []int
声明的切片默认值为nil
,但是可以直接添加元素,不会报错,如s = append(s,10)
-
切片可以从数组或者切片中,截取一段,返回一个新的切片
-
切片截取语法为:
[start: end]
,前包括后不包括,如s1 := s[1:3]
取 s 中的 索引为 0,1,2 的元素 -
切片的访问通过索引,索引从 0 开始
-
向切片添加元素,使用内置的
append()
函数,注意,append()
返回的切片必须重新赋值给原来的切片如
s = append(s,10)
, 必须重新赋值给s
-
切片的长度和容量分别使用
len()
函数和cap()
函数