切片

    类型 就是一个由类型 T 元素组成的切片。

    切片通过两个索引下标定义,一个表示 下边界 ( low bound ),一个表示 上边界 ( high bound ),以冒号( )分隔:

    这表示一个 半开半闭 区间,包括第一个元素,但不包括最后一个。

    以下表达式创建一个包含元素 1 到元素 3 的切片(不包括元素 4):

    1. a[1:4]

    完整例子如下:

    /_src/tour/slices.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5.  
    6. func main() {
    7. primes := [6]int{2, 3, 5, 7, 11, 13}
    8.  
    9. var s []int = primes[1:4]
    10. fmt.Println(s)
    11. }

    切片实际上并 不存储任何数据 ,只是用来描述关联数组的一部分。因此,修改切片元素等价于修改对应的数组元素,其他共用该数组的切片对此也可见。换句话讲, 切片就像是数组的引用

    /_src/tour/slice-pointers.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5.  
    6. func main() {
    7. names := [4]string{
    8. "John",
    9. "Paul",
    10. "George",
    11. "Ringo",
    12. }
    13. fmt.Println(names)
    14.  
    15. a := names[0:2]
    16. b := names[1:3]
    17. fmt.Println(a, b)
    18.  
    19. b[0] = "XXX"
    20. fmt.Println(a, b)
    21. fmt.Println(names)
    22. }

    下标默认值

    定义切片,可以忽略上边界或者下边界,而使用 默认值 。对于下边界,默认值为 0 ;下边界,默认值则是 切片长度

    因此,对于数组:

    1. var a [10]int

    以下切片表达式均是等价的:

    1. a[0:10]
    2. a[:10]
    3. a[0:]
    4. a[:]

    /_src/tour/slice-bounds.go

    长度和容量

    切片有两个重要属性, 长度 ( length )和 容量 ( capacity )。

    切片的长度等于切片包含的元素个数。

    切片的容量等于底下数组的元素个数,从切片第一个元素算起。

    对于切片 s ,长度和容量分别可以通过表达式 len(s) 以及 获得。

    通过 重切 ( re-slicing ),你可以扩张一个切片的长度,只要容量足够。你可以试试修改下面这个例子,将切片扩张到超出容量,看看会发生什么事情:

    /_src/tour/slice-len-cap.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5.  
    6. func main() {
    7. s := []int{2, 3, 5, 7, 11, 13}
    8.  
    9. // Slice the slice to give it zero length.
    10. s = s[:0]
    11. printSlice(s)
    12.  
    13. // Extend its length.
    14. s = s[:4]
    15. printSlice(s)
    16.  
    17. // Drop its first two values.
    18. s = s[2:]
    19. printSlice(s)
    20. }
    21.  
    22.  
    23. func printSlice(s []int) {
    24. fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
    25. }

    /_src/tour/nil-slices.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5.  
    6. func main() {
    7. var s []int
    8. fmt.Println(s, len(s), cap(s))
    9. if s == nil {
    10. fmt.Println("nil!")
    11. }
    12. }

    make

    切片可以由内置函数 make 来创建,相当于你可以创建动态长度的数组。

    make 函数分配一个由 填充的数组,并返回一个引用该数组的切片:

    1. a := make([]int, 5) // len(a)=5

    要指定容量,可以通过 make 函数第三个参数指定:

    1. b := make([]int, 0, 5) // len(b)=0, cap(b)=5
    2.  
    3. b = b[:cap(b)] // len(b)=5, cap(b)=5
    4. b = b[1:] // len(b)=4, cap(b)=4

    完整例子如下:

    /_src/tour/making-slices.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5. func main() {
    6. a := make([]int, 5)
    7. printSlice("a", a)
    8.  
    9. b := make([]int, 0, 5)
    10. printSlice("b", b)
    11.  
    12. c := b[:2]
    13. printSlice("c", c)
    14.  
    15. d := c[2:5]
    16. printSlice("d", d)
    17. }
    18.  
    19. func printSlice(s string, x []int) {
    20. fmt.Printf("%s len=%d cap=%d %v\n",
    21. s, len(x), cap(x), x)
    22. }

    切片的切片

    切片可以包含任何类型,当然包括其他切片。

    /_src/tour/slices-of-slices.go

    向切片追加元素是一个很常用的操作,为此 Go 提供了一个 内置函数 。 详细描述了这个内置函数 append

    1. func append(s []T, vs ...T) []T

    append 函数第一个参数 s 是一个类型为 T 的切片,其余参数均为追加至 sT 元素。

    append 函数返回一个新切片,包含原切片以及所有追加元素。

    如果底层数组太小, append 函数会分配一个更大的数组,新切片则指向新数组。

    /_src/tour/append.go

    1.  
    2. import "fmt"
    3.  
    4. func main() {
    5. var s []int
    6. printSlice(s)
    7.  
    8. // append works on nil slices.
    9. s = append(s, 0)
    10. printSlice(s)
    11.  
    12. // The slice grows as needed.
    13. s = append(s, 1)
    14. printSlice(s)
    15.  
    16. // We can add more than one elements at a time.
    17. s = append(s, 2, 3, 4)
    18. printSlice(s)
    19. }
    20.  
    21. func printSlice(s []int) {
    22. fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
    23. }

    遍历

    配合 range 关键字, for 循环可对切片进行 遍历 。下节,我们将看到,这种做法也适用于 ( map )。

    每次迭代都返回两个值,第一个是 下标 ( index ),第二个是与该下标对应的 元素拷贝

    /_src/tour/range.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5. var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
    6.  
    7. func main() {
    8. for i, v := range pow {
    9. fmt.Printf("2**%d=%d\n", i, v)
    10. }
    11. }

    如果无须下标,可以直接赋值给下划线 _ 。如果只要下标,则不写后半部分即可。

    /_src/tour/range-continued.go

    1. package main
    2.  
    3. import "fmt"
    4.  
    5. func main() {
    6. pow := make([]int, 10)
    7.  
    8. for i := range pow {
    9. pow[i] = 1 << uint(i) // == 2**i
    10. }
    11.  
    12. for _, value := range pow {
    13. fmt.Printf("%d\n", value)
    14. }
    15. }

    练习

    当你运行这个程序时,它将展示一张图片,每个数值被解释为图片像素的 灰度值 ( grayscale )。

    代码框架如下:

    /_src/tour/exercise-slices-pic.go

    1. package main
    2.  
    3. import "golang.org/x/tour/pic"
    4.  
    5. func Pic(dx, dy int) [][]uint8 {
    6. }
    7.  
    8. func main() {
    9. pic.Show(Pic)

    你可以在 Go官网 进行练习。图片长啥样取决于你返回的切片。试试以下以下公式来生成切片,结果将非常有趣:

    [\frac{x+y}{2}]

    [x * y]

    [x ^ y]

    这三个公式生成的图片分别是:

    (x+y) / 2

    ../_images/7261fd8ba2ae27e30e9fcf93a1ed900e.pngx * y

    x ^ y

    答案

    相信你已经写出自己的程序,看到各种有趣的图片了。

    如果练习过程中遇到什么问题,可以参考下面这个例子并尝试解决:

    /_src/tour/solution-slices-pic.go

    我们一起来看看 Go 语言 映射表

    订阅更新,获取更多学习资料,请关注我们的 :

    ../_images/wechat-mp-qrcode.png小菜学编程