拥有相同类型元素的可变长度序列。通常写作[]T, 元素类型都T, 可以访问数组的部分或全部元素,这个数组称为slice底层数组。
属性
slice有三个属性:指针、长度和容量。
指针指向数组的第一个可以从slice中访问的元素。
长度是slice中元素的个数,不能超过slice的容量。
容量是从slice的起始元素到底层数组的最后一个元素间的元素个数。
slice操作符 s[i:j] (其中i<=j<=cap(s)) 创建了一个新的slilce,引用了序列S中从i到j-1索引位置的所有元素。
S可以是数组、指向数组的指针、或slice.
如果表达式省略了i,则i = 0; 如果省略了j,则j = len(s)
因为slice包含了指向数组的指针,所以将一个slice传递给函数,可以在函数内部修改底层数组的元素。
//就地反转一个整型slice中的元素
func reverse (s []int){
for i,j := 0,len(s) - 1; i, j = i +1, j -1 {
s[i], s[j] = s[j], s[i]
}
}
a := [...]int{0,1,2,3,4,5}
reverse( a[:] )
初始化slice
用逗号分隔并用花括号括起来的元素序列,但slice并没有指定长度。
如何初始化slice:
//方法1:用make
var s1 []string = make([]string, 0)
var s2 []string = make([]string, 1)
var s3 []string = make([]string, 20)
方法2:静态显式初始化
var s1 []string = []string {}
var s2 []string = []string{"aa"}
var s3 []string = []string{"aa", "bb", "cc"}
var s4 = []string{}
var s5 = []string{"aa"}
var s6 = []string{"aa", "bb", "cc"}
s7 := []string{}
s8 := []string{"aa"}
s9 := []string{"aa", "bb", "cc"}
这种区别是,创建固有长度的数组 和 指向数组的slice.
slice无法作比较,因为不能用 == 来测试两个slice是否拥有相同的元素,标准库里提供了bytes.Equal来比较两个字节slice([]byte).
string的slice比较:
func equal (x,y []string) bool{
if len(x) != len(y){
return false
}
for i := 0; i < len(x); i++{
if x[i] !=y[i]
return false
}
return true
}
为什么不可以直接用 == 比较 ?
1.slice元素是非直接的,有可能包含它自身;
2.如果底层数组元素改变,同一个slice在不同时间会有不同的元素。由于散列表仅对元素的键做浅拷贝,这要求散列表里键在散列表的整个生命周期内必须保持不变。因为slice需要做深度比较,不能用slice做map的键。
nil值比较
slice唯一允许的比较操作符是和nil做比较,slice类型的零值是nil,表示没有对应的底层数组;
一个nil值的slice的长度和容量是0;
也有非nil值的slice的长度和容量是0,如,[]int{} 或 make ([]int,3)[3:] ;
对任何类型,如果它们的值可以为nil,那这个类型的nil值,可使用转换表达式,如:[]int(nil)
var s []int //len(s) == 0, s == nil
s = nil //len(s) == 0, s== nil
s = []int(nil) //len(s) == 0, s == nil
s = []int{} //len(s) == 0, s != nil
检查一个slice是否为空,使用 len(s) == 0, 而不是 s == nil
内置函数make可以创建一个指定元素类型、长度和容量的slice,容量参数可以省略,此时长度和容量相等。
make ([]T,len)
make([]T, len, cap)
make创建了一个无名数组并返回了它的一个slice,这个数组仅可以通过这个slice访问。
//为rune类型添加元素,追加到slice后面:
var runes []rune
for _,r := range "Hello,世界"{
runes = append(runes, r)
}
fmt.Printf("%q\n", runes)
//最方便的用法是 []rune("Hello,世界")
分析:slice添加元素
func appendInt (x []int, y int) []int{
var z []int
zlen := len(x) + 1
if zlen < cap(x){
//slice仍有增长空间,扩展slice内容
z = x[:len]
} else{
//slice已无空间容量, 分配一个新的底层数组
zcap := zlen
if zcap < 2 * len(x){
zcap = 2 * len(x)
}
z = make([]int, zlen, zcap)
copy(z, x) //内置copy函数
}
z[len(x)] = y
return z
}
容量足够:定义一个新的slice(仍引用原底层数组),将新元素复制到新的位置,并返回新slice.
【指向原底层数组的新slice】
容量不够:必须创建一个拥有足够容量的新的底层数组,然后将元素从原slice复制到这个数组,再将y追加到数组后面。
【指向新底层数组的新lsice】
copy函数
第一个参数是目标slice,第二个参数是原slice.
返回值:返回实际上复制的元素个数,这个值是两个slice长度较小的值。
遍历
slice := []int{0, 1, 2, 3}
for k,v :=range slice{
fmt.Println("k:",k,"v:",v)
}
for range遍历的内容是对原内容的一个拷贝,所以不能用来修改原切片中内容;
如果要修改原slice的值,需要使用k根据索引位置直接修改:
for k,v :=range slice{
if v==1 {
slice[k]=100
}
}