Go的slice

经常用C中的数组,就会知道数组存在一个不足,那就是一旦容量分配了,那就是固定长度,无法再更改.

而Golang中的slice并不是数组或数组指针,其本质上是一个数组的某个部分的引用。在源码runtime/slice.go源码中定义了slice的结构:

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

可以看到slice是一个包含3个字段的结构体:

  • array: 指向slice第一个元素的指针

  • len: slice的长度,长度是索引操作的上届, s[i] i<len(s)

  • cap: slice的容量,容量是分割操作的上届, s[i:j] j<=cap(s)

slice的使用

slice和数组的区别:

声明数组时,方括号内写明了数组的长度或使用自动计算长度;而声明slice时,方括号内没有任何字符.

    var s1 []int //声明切片和声明array一样,只是少了长度,此为空(nil)切片
    s2 := []int{}
    //make([]T, length, capacity) //capacity可以省略,则和length的值相同
    var s3 []int = make([]int, 0)
    s4 := make([]int, 0, 0)
    s5 := []int{1, 2, 3} //创建切片并初始化

内建函数append的使用:

    var s1 []int
    s1 = append(s1, 1)       //追加1个元素
    s1 = append(s1, 2, 3)    //追加2个元素
    fmt.Println(s1) //[1,2,3]
    s2 := make([]int, 5)
    s2 = append(s2, 6)
    fmt.Println(s2) //[0 0 0 0 0 6]
    s3 := []int{1, 2, 3}
    s3 = append(s3, 4, 5)
    fmt.Println(s3)//[1 2 3 4 5]

append的实现:

func growslice(et *_type, old slice, cap int) slice {
    //......
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            for newcap < cap {
                newcap += newcap / 4
            }
        }
    }
    //......
    return slice{p, old.len, newcap}
}

从以上源码可以看出在扩容时:

  • 如果容量需求大于原cap的两倍以上,则扩容后容量即为需求的容量。

  • 如果原来的len小于1024,则扩容后的cap为原cap的两倍。即按原cap 2倍增长扩容。

  • 如果原来的len大于等于1024,按1/4增长计算新的cap,直到满足容量需求。

内建函数copy的使用:

    data := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    s1 := data[8:]  //{8, 9}
    s2 := data[:5] //{0, 1, 2, 3, 4}
    copy(s2, s1)    // dst:s2, src:s1
    fmt.Println(s2)   //[8 9 2 3 4]
    fmt.Println(data) //[8 9 2 3 4 5 6 7 8 9]

可以看出copy在两个 slice 间复制数据,复制⻓度以 len 小的为准,两个 slice 可指向同⼀底层数组。

slice做参数:

func test(s []int) {
    s[0] = -1
    fmt.Println("test : ")
    for i, v := range s {
        fmt.Printf("s[%d]=%d, ", i, v)
    }
    fmt.Println("\n")
}

func main() {
    slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    test(slice)
    fmt.Println("main : ")
    for i, v := range slice {
        fmt.Printf("slice[%d]=%d, ", i, v)
    }
    fmt.Println("\n")
}