go基础第五篇:slice扩容

1、

func main() {
    var s = make([]int, 5, 8)
    for i := 0; i < len(s); i++ {
        s[i] = i + 1
    }
    fmt.Println(s, len(s), cap(s))
    var s2 = s
    var s3 = s[:]
    var s4 = s[:3]
    var s5 = s[4:]
    var s6 = s[2:4]
    fmt.Println(s2, len(s2), cap(s2))
    fmt.Println(s3, len(s3), cap(s3))
    fmt.Println(s4, len(s4), cap(s4))
    fmt.Println(s5, len(s5), cap(s5))
    fmt.Println(s6, len(s6), cap(s6))
    s[0] = 255
    fmt.Println(s, len(s), cap(s))
    fmt.Println(s2, len(s2), cap(s2))
    fmt.Println(s3, len(s3), cap(s3))
    fmt.Println(s4, len(s4), cap(s4))
    fmt.Println(s5, len(s5), cap(s5))
    fmt.Println(s6, len(s6), cap(s6))
}

把s赋值给s2后,s2和s是两个不同的切片(可以通过%p打印出变量地址),但共享底层数组,长度和容量均相同。切片的结构(见内置的SliceHeader)是一个三元组(指向底层数组的指针、长度、容量),赋值时,这三项会被逐一复制。

通过截取获得的slice,与原slice共享底层数组。故s3、s4、s5、s6与s共享底层数组。

cap(slice)返回的是从slice起始位置到底层数组末尾的元素数量,即底层数组长度-切片起始索引。s2的起始位置是底层数组的索引0,

2、 

func main() {
    x := []int{1, 2, 3, 4}
    y := x[:2]
    fmt.Println(x, cap(x))
    fmt.Println(y, cap(y))

    y = append(y, 30)
    fmt.Println(x, cap(x))
    fmt.Println(y, cap(y))

    y = append(y, 31)
    fmt.Println(x, cap(x))
    fmt.Println(y, cap(y))

    y = append(y, 32)
    fmt.Println(x, cap(x))
    fmt.Println(y, cap(y))

    y = append(x[1:3], 33)
    fmt.Println(x, cap(x))
    fmt.Println(y, cap(y))

    y = append(x[1:3], 34, 35)
    fmt.Println(x, cap(x))
    fmt.Println(y, cap(y))
}

如果append操作的slice的底层数组中在slice之后有元素,则append将覆盖slice之后的元素,append一个元素就覆盖一个元素,append两个元素就覆盖两个元素。append操作slice,如果要扩容,则只会把slice中的元素复制到新数组中,slice之前的元素不会复制。

最后y的容量为什么是6???是因为x[1:3]的容量是3吗?

 

3、 

func main() {
    x := []int{1, 2, 3, 4}
    y := x[2:]
    fmt.Println(cap(x), cap(y))
    y = append(y, 30)
    fmt.Println("x:", x)
    fmt.Println("y:", y)
}

4、

func main() {
    var s1 = []int{1, 2, 3, 4, 5}
    fmt.Println(s1, len(s1), cap(s1))
    var s2 = append(s1, 6)
    fmt.Println(s1, len(s1), cap(s1))
    fmt.Println(s2, len(s2), cap(s2))
    var s3 = append(s2, 7)
    fmt.Println(s2, len(s2), cap(s2))
    fmt.Println(s3, len(s3), cap(s3))
}

s2的容量为什么是10?扩容后容量是怎么变化的?

扩容机制见runtime包中slice.go文件(即slice struct所在文件)的growslice函数,主要分为两步:

第一步:调用nextslicecap函数,返回值是新数组的容量。nextslicecap函数的逻辑是:如果旧容量<256,则返回max(2*旧容量, slice新长度)。否则,按照按照0.25*旧容量+3/4*256的逻辑递增,直到大于slice新长度。newcap += (newcap + 3*threshold) >> 2。

第二步:内存对齐。在第一步的基础上,根据数组中元素所占字节数的不同,执行不同的逻辑,得到最终的新容量。

5、

func main() {
    s1 := make([]int, 6)
    s2 := make([]int, 1024)
    s1 = append(s1, 1)
    s2 = append(s2, 2)
    fmt.Println(len(s1), cap(s1))
    fmt.Println(len(s2), cap(s2))
}

 

6、

func main() {
    var s1 []int
    var s2 = []int{}
    var s3 = make([]int, 0)
    var s4 = *new([]int)
    fmt.Println(s1, s2, s3, s4)
    fmt.Println(s1 == nil, s2 == nil, s3 == nil, s4 == nil)
    fmt.Println(reflect.DeepEqual(s1, s4), reflect.DeepEqual(s2, s3))
    fmt.Println(len(s1), len(s2), len(s3), len(s4))
    fmt.Println(cap(s1), cap(s2), cap(s3), cap(s4))
}

比较两个slice是否相同,可以直接调用reflect.DeepEqual(s1, s2)进行比较。如果一个是nil分片(如var s1 int[]),一个是空分片(如s2 :=[]int{}),会返回false。

posted on 2024-06-25 09:57  koushr  阅读(14)  评论(0)    收藏  举报

导航