Go入门指南(切片) | 豆包MarsCodeAI刷题

150 阅读3分钟

切片(slice)

切片是Go语言对动态数组的一种实现,切片可以动态扩容,比数组具有更高的灵活性
下文以slice代称

1. 声明

slice的声明有两种方式

  1. 字面量声明 arr = []int{}
  2. make函数声明 arr := make([]int, 0)
package main

import "fmt"

func main() {
    arr0 := []int{}
    arr1 := make([]int, 1)            //创建了一个长度为1, 容量为1的slice
    arr2 := make([]int, 1, 2)         //创建了一个长度为1, 容量为2的slice
    fmt.Println(len(arr0), cap(arr0)) // 0 0
    fmt.Println(len(arr1), cap(arr1)) // 1 1
    fmt.Println(len(arr2), cap(arr2)) // 1 2
}

此处的一些细节

  • make函数创建slice时,必须指明长度,make([]int, 0) == []int{}
  • 容量是可选参数,不指明时 len(arr) == cap(arr)
  • 长度和容量的区别

长度是指当前slice实际可以存储的元素个数,容量是指底层存储空间的最大元素个数
当长度到达容量时,底层会触发扩容机制,自动分配一个新的底层数组,并将旧数据复制到新的数组中

2. 基本操作

  • 追加元素

使用append()函数追加 arr = append(arr,1,2,3)
也可以追加一个切片 arr = append(arr, arr1...)

  • 修改元素

通过下标(从0开始)进行修改即可, arr[1] = 2

  • 切片
    arr[ start : end : max ]
    start:切片的起始索引(包含),省略则从0开始
    end:切片的结束索引(不包含),省略则到len(arr)结束
    max:新的切片的容量上限,表示从 startmax 的最大索引范围(不包含 max 本身)。
nums := []int{1, 2, 3, 4, 5}
sub := nums[1:3:4] // 从索引 1 到 3 的切片,最大容量为索引 4
fmt.Println(sub)      // 输出:[2, 3]
fmt.Println(len(sub)) // 输出:2,长度为 3-1
fmt.Println(cap(sub)) // 输出:3,容量为 4-1

max参数限制其最大容量,控制切片的增长,防止对原始切片底层数组的某些意外修改。
Go语言的切片是O(1)的(速度很快),因为底层是直接改变指针,而不是复制一个处理,举个例子

package main

import "fmt"

func main() {
    nums := []int{1, 2, 3, 4, 5}
    a := nums[:3] // [1,2,3]

    //a[0] = 0
    //fmt.Println(nums) // [0 2 3 4 5]

    a = append(a, 100)
    fmt.Printf("%v, %p\n", a, a)       //[1 2 3 100], 0xc00000e3c0
    fmt.Printf("%v, %p\n", nums, nums) //[1 2 3 100 5], 0xc00000e3c0
    nums = []int{1, 2, 3, 4, 5}
    b := nums[:3:3]                    //加了max限制
    b = append(b, 200)                 //此时扩容就是新切片,而不是nums的引用
    fmt.Printf("%v, %p\n", b, b)       //[1 2 3 200], 0xc0000a8090
    fmt.Printf("%v, %p\n", nums, nums) //[1 2 3 4 5], 0xc0000a8060
}

简单来说,切片不是副本,他们会共享相同的底层数组,我们改变切片后会导致原slice发生变化
使用max参数时,可以解决追加元素导致的问题,但不能解决修改元素的问题
想复制一个切片需要使用copy函数

package main

import "fmt"

func main() {
    nums := []int{1, 2, 3, 4, 5}
    a := make([]int, 2)
    copy(a, nums)  //保证len(a) > 0 会复制min(len(a), len(nums))个元素到a中
    fmt.Println(a) // [1,2]
    a[0] = 0
    fmt.Println(a)    // [0,2]
    fmt.Println(nums) // [1,2,3,4,5]
}

有一些连续操作:比如a := nums[:3][:1]

  • 遍历
    我们可以使用for i, v := range nums来遍历一个slice,修改v不会对slice造成变化,修改nums[i]会产生变化
package main

import "fmt"

func main() {
    nums := []int{1, 2, 3, 4, 5}
    for _, v := range nums {
       v = 0 // v只是值拷贝
       fmt.Println(v)
    }
    fmt.Println(nums) //[1 2 3 4 5]
    for i := range nums {
       nums[i] = 0
    }
    fmt.Println(nums) //[0 0 0 0 0]

}