• go的切片扩容机制


    切片的扩容策略?如何扩容?

    扩容策略:如果切片的容量小于 1024 个元素,于是扩容的时候就翻倍增加容量。总容量从原来的1个翻倍到现在的2个。

    一旦元素个数超过 1024 个元素,那么增长因子就变成 1.25 ,即每次增加原来容量的四分之一。

    注意:扩容扩大的容量都是针对原来的容量而言的,而不是针对原来数组的长度而言的。

    举一个扩容策略例子:

    func main() {
        slice := []int{10, 20, 30, 40}
        newSlice := append(slice, 50)
        fmt.Printf("Before slice = %v, Pointer = %p, len = %d, cap = %d\n", slice, &slice, len(slice), cap(slice))
        fmt.Printf("Before newSlice = %v, Pointer = %p, len = %d, cap = %d\n", newSlice, &newSlice, len(newSlice), cap(newSlice))
        newSlice[1] += 10
        fmt.Printf("After slice = %v, Pointer = %p, len = %d, cap = %d\n", slice, &slice, len(slice), cap(slice))
        fmt.Printf("After newSlice = %v, Pointer = %p, len = %d, cap = %d\n", newSlice, &newSlice, len(newSlice), cap(newSlice))
    }
    
    // result 
    Before slice = [10 20 30 40], Pointer = 0xc4200b0140, len = 4, cap = 4
    Before newSlice = [10 20 30 40 50], Pointer = 0xc4200b0180, len = 5, cap = 8
    After slice = [10 20 30 40], Pointer = 0xc4200b0140, len = 4, cap = 4
    // 相加的数是在新的切片上相加的
    After newSlice = [10 30 30 40 50], Pointer = 0xc4200b0180, len = 5, cap = 8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    从结果我们可以看出,新切片和之前的切片已经不同了,因为新的切片更改了一个值,并没有影响到原来的数组,新切片指向的数组是一个全新的数组,并且cap容量也发生了变化。

    那么到底是新数组还是老数组呢?

    扩容之后数组就一定是新的吗?其实是分两种情况。

    第一种情况:

    func main() {
        array := [4]int{10, 20, 30, 40}
        slice := array[0:2]
        newSlice := append(slice, 50)
        fmt.Printf("Before slice = %v, Pointer = %p, len = %d, cap = %d\n", slice, &slice, len(slice), cap(slice))
        fmt.Printf("Before newSlice = %v, Pointer = %p, len = %d, cap = %d\n", newSlice, &newSlice, len(newSlice), cap(newSlice))
        newSlice[1] += 10
        fmt.Printf("After slice = %v, Pointer = %p, len = %d, cap = %d\n", slice, &slice, len(slice), cap(slice))
        fmt.Printf("After newSlice = %v, Pointer = %p, len = %d, cap = %d\n", newSlice, &newSlice, len(newSlice), cap(newSlice))
        fmt.Printf("After array = %v\n", array)
    }
    
    // result
        Before slice = [10 20], Pointer = 0xc4200c0040, len = 2, cap = 4
        Before newSlice = [10 20 50], Pointer = 0xc4200c0060, len = 3, cap = 4
        After slice = [10 30], Pointer = 0xc4200c0040, len = 2, cap = 4
        After newSlice = [10 30 50], Pointer = 0xc4200c0060, len = 3, cap = 4
        After array = [10 30 50 40]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    通过这个结果可以明显的看出,修改新切片的值居然影响到了老切片的值了,扩容以后并没有新建一个新的数组,并且append操作也改变了原来数组的值,如果原数组上有多个切片,那么这些切片都会影响,产生了莫名的bug!

    这种情况也极容易出现在字面量创建切片时候,第三个参数 cap 传值的时候,如果用字面量创建切片,cap 并不等于指向数组的总容量,那么这种情况就会发生。

    注意:建议用字面量创建切片的时候,cap 的值一定要保持清醒,避免共享原数组导致的 bug。

    第二种情况:

    ​ 第二种情况就是扩容策略的例子了,在扩容之后产生了新的切片,因为原数组的容量达到了最大值,再扩容都会开出一片新的内存,把原来的值拷贝过来,再进行后续操作也不会影响原数组。

    总结:时刻保持cap的清醒,推荐使用第二种情况的扩容策略

  • 相关阅读:
    SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.11 jetcache 方法缓存
    二极管:Irush与我何干?
    MyBatis-plus 代码生成器配置
    PC电脑 VMware安装的linux CentOs7如何扩容磁盘?
    Java函数式编程
    【阿旭机器学习实战】【12】决策树基本原理及其构造与使用方法
    从接口中返回二进制数据
    2024年网络安全/黑客自学路线图
    Python自动化处理Excel数据
    MongoDB 未授权访问漏洞
  • 原文地址:https://blog.csdn.net/weixin_51991615/article/details/127657641