Slice的基本使用 1 2 3 4 5 6 7 8 9 10 11 12 package mainimport "fmt" func main () { arr := [...]string {"a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" } fmt.Println("arr[2:6]" , arr[2 :6 ]) fmt.Println("arr[:6]" , arr[:6 ]) fmt.Println("arr[2:]" , arr[2 :]) fmt.Println("arr[:]" , arr[:]) }
执行结果:
1 2 3 4 arr[2:6] [c d e f] arr[:6] [a b c d e f] arr[2:] [c d e f g h] arr[:] [a b c d e f g h]
注意: 左开右闭区间; Slice是对数组的View(视图)
Slice是引用类型 由于Slice是对底层数组的视图, 当Slice中的元素被修改后, 同样的, 底层数组中的元素也将被修改
看以下demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func main () { arr := [...]string {"a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" } s := arr[2 :6 ] fmt.Println("原始s变量的值" , s) fmt.Println("原始数组的值" , arr) s[0 ] = "xx" fmt.Println("修改后变量的值" , s) fmt.Println("修改后数组的值" , arr) }
执行结果:
1 2 3 4 原始s变量的值 [c d e f] 原始数组的值 [a b c d e f g h] 修改后变量的值 [xx d e f] 修改后数组的值 [a b xx d e f g h]
Slice & Array 切片和数组的写法很相似, 这里把他们放到一起区分以下
1 2 3 4 5 6 7 func funcName (arr [5]int ) // 接收一个长度为5的int 类型的数组(值传递) func funcName (arr *[5]int ) // 接收一个长度5的int 类型的数组的指针(引用传递) func funcName (sli []int ) // 接收一个Slice 切片(引用传递)
Reslice Slice可以在Slice的基础上再次Slice, 创建Slice的视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport "fmt" func main () { arr := [...]string {"a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" } s := arr[2 :6 ] fmt.Println("数组arr的值" , arr) fmt.Println("s切片的值" , s) s2 := s[:3 ] s3 := s[2 :] fmt.Println("s2, 基于s切片的view" , s2) fmt.Println("s3, 基于s切片的view" , s3) }
执行结果:
1 2 3 4 数组arr的值 [a b c d e f g h] s切片的值 [c d e f] s2, 基于s切片的view [c d e] s3, 基于s切片的view [e f]
上面的Demo中, 即使有多个Slice, 但其本质, 还是view了同一个Array
Slice的扩展 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func main () { arr := [...]string {"a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" } s := arr[2 :6 ] fmt.Println("数组arr的值" , arr) fmt.Println("s切片是对arr的view, 值为" , s) ss := s[3 :5 ] fmt.Println("ss切片是对s切片的view, 值为" , ss) }
执行结果:
1 2 3 数组arr的值 [a b c d e f g h] s切片是对arr的view, 值为 [c d e f] ss切片是对s切片的view, 值为 [f g]
上面的Demo中, 出现了一个很有意思的问题, 就是ss切片, 根据上面介绍的理论, ss切片是对s切片的view, 也就是Reslice, 但是s切片中只有4个值, 元素索引为0-3
, 而ss对其进行reslice后, 却要看3-5
的索引, 如果是普通Array的话, 肯定会报索引越界的异常, 但是上面Demo我们看出, 代码并没有抛出任何异常, 不仅还取到了值, 还取到了s切片中不存在的值
1 2 3 4 5 6 7 8 9 "a", "b", "c", "d", "e", "f", "g", "h" <== Array 0 1 2 3 4 5 6 7 <== Array Index "c", "d", "e", "f" <== Slice arr[2:6] 0 1 2 3 4 5 <== Sline Index "f", "g" <== Reslice sli[3:5] 0 1 2 <== Reslice Index
Go语言Slice的扩展特性: 在Slice后, Array后面的元素依然可以被Slice感知, 但是Slice后面这些元素不能直接被取出, 直接取值会报索引越界的错误. 但是当Slice被Reslice时, Slice后面隐藏的值是可以在Reslice中重见天日的
Slice的实现 假设: - - - -
为Slice对Array的视图
1 2 3 4 5 Array: `+ + + + - - - - ~ ~ ~ ~` ↑ | | ptr | | |-len-| | |-----cap-----|
Slice内部有三个元素:
ptr: 指向了Slice开头的元素. 依据上图, 也就是指向了第一个-
len: 保存了Slice的长度, 使用 sli[x] 这种方式取值, 只能取到sli[len-1]. 依据上图, len保存了-
的长度, 4
cap: cap保存了从ptr开始到Array数组结束的长度. 依据上图, 也就是说cap保存了从第一个-
开始, 到最后一个~
为止的长度, 8
所以Go知道, Slice在扩展的时候, 只要不超过cap的长度, 就是可以取到的
注意: Slice可以向后扩展, 但是不能向前扩展, 图示中+
部分的值在Slice中永远不会被取出来, 除非重新使用Slice对Array进行view操作`
sli[x]取值不可以超越len(sli); 向后扩展, 不可以超越底层Array cap(sli)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { arr := [...]string {"a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" } fmt.Println("Array数组的cap长度为: " , cap (arr)) s := arr[2 :6 ] fmt.Println("数组arr的值" , arr) fmt.Println("s切片是对arr的view, 值为" , s) fmt.Println("s切片的len长度和cap长度分别为: " , len (s), cap (s)) ss := s[3 :5 ] fmt.Println("ss切片是对s切片的view, 值为" , ss) fmt.Println("ss切片的len长度和cap长度分别为: " , len (ss), cap (ss)) }
执行结果:
1 2 3 4 5 6 Array数组的cap长度为: 8 数组arr的值 [a b c d e f g h] s切片是对arr的view, 值为 [c d e f] s切片的len长度和cap长度分别为: 4 6 ss切片是对s切片的view, 值为 [f g] ss切片的len长度和cap长度分别为: 2 3