通过切片创建新的切片

切片之所以被称为切片,是因为创建一个新的切片,也就是把底层数组切出一部分。通过切片创建新切片的语法如下:

  1. slice[i:j]
  2. slice[i:j:k]

其中

  • i 表示从 slice 的第几个元素开始切
  • j 表示切到第几个元素(不包括)。切片的长度为 (j-i)
  • k 控制切片的容量(k-i),如果没有给定 k,则表示切到底层数组的最尾部。

下面是几种常见的简写形式:

  1. slice[i:] // 从 i 切到最尾部
  2. slice[:j] // 从最开头切到 j(不包含 j)
  3. slice[:] // 从头切到尾,等价于复制整个 slice

让我们通过下面的例子来理解通过切片创建新的切片的本质:

  1. // 创建一个整型切片
  2. // 其长度和容量都是 5 个元素
  3. myNum := []int{10, 20, 30, 40, 50}
  4. // 创建一个新切片
  5. // 其长度为 2 个元素,容量为 4 个元素
  6. newNum := slice[1:3]

执行上面的代码后,我们有了两个切片,它们共享同一段底层数组,但通过不同的切片会看到底层数组的不同部分:

通过切片创建新的切片 - 图1

:::warning 注意,截取新切片时的原则是 “左含右不含”。

:::

所以 newNum 是从 myNumindex=1处开始截取,截取到 index=3 的前一个元素,也就是不包含 index=3 这个元素。所以,新的 newNum 是由 myNum 中的第 2 个元素、第 3 个元素组成的新的切片构,长度为 2,容量为 4

切片 myNum 能够看到底层数组全部 5 个元素的容量,而 newNum 能看到的底层数组的容量只有 4 个元素。newNum 无法访问到底层数组的第一个元素。所以,对 newNum 来说,那个元素就是不存在的。

共享底层数组的切片

需要注意的是:

:::danger 现在两个切片 myNumnewNum 共享同一个底层数组。如果一个切片修改了该底层数组的共享部分,另一个切片也能感知到 (请参考前图):

:::

  1. // 修改 newNum 索引为 1 的元素
  2. // 同时也修改了原切片 myNum 的索引为 2 的元素
  3. newNum[1] = 35

把 35 赋值给 newNum 索引为 1 的元素的同时也是在修改 myNum 索引为 2 的元素:

通过切片创建新的切片 - 图2

切片只能访问到其长度内的元素

切片只能访问到其长度内的元素,试图访问超出其长度的元素将会导致语言运行时异常。在使用这部分元素前,必须将其合并到切片的长度里。下面的代码试图为 newNum 中的元素赋值:

  1. // 修改 newNum 索引为 3 的元素
  2. // 这个元素对于 newNum 来说并不存在
  3. newNum[3] = 45

上面的代码可以通过编译,但是会产生运行时错误:

panic: runtime error: index out of range