Slice的基本使用
1 2 3 4 5 6 7 8 9 10 11 12
| package main
import "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 main
import "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 main
import "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 main
import "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 main
import "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
|