Go语言是如何实现切片扩容的?

发布于 1周前 作者 yibo5220 最后一次编辑是 5天前 来自 问答

Go语言是如何实现切片扩容的? 切片的扩容是Go语言运行时(runtime)自动进行的一个过程,当向切片追加元素导致其长度超过容量时,Go会创建一个新的、更大的底层数组,并将旧数组的内容复制到新数组中,然后更新切片的指针、长度和容量。

下面是Go语言切片扩容的示例代码

func main() {
arr := make([]int, 0)
for i := 0; i < 2000; i++ {
fmt.Println("len 为", len(arr), "cap 为", cap(arr)) arr = append(arr, i)
} }

我们可以看下结果 依次是
0,1,2,4,8,16,32,64,128,256,512,1024
但到了 1024 之后,就变成了 1024,1280,1696,2304
每次都是扩容了四分之一左右
2 回复

在Go语言中,切片(slice)是对数组的抽象,提供了一种灵活的方式来操作一段连续的元素序列。切片本身并不存储数据,而是描述了一个底层数组(通常是数组的一个子序列)的起始位置、长度和容量。当切片的内容超出其当前容量时,就需要进行扩容以存储更多的元素。

Go语言标准库中对切片的扩容操作是自动的,具体的扩容逻辑依赖于运行时(runtime)的实现,但大致遵循以下规则:

  1. 如果切片的新长度小于或等于当前容量的两倍,则容量加倍。
  2. 如果切片的新长度大于当前容量的两倍,则新容量等于新长度。
  3. 如果切片当前容量小于1024个元素,则扩容时采用翻倍策略。
  4. 一旦切片元素数量超过1024,扩容策略会变得更加谨慎,每次扩容增长的幅度会减小,以节省内存。

以下是一个简化的示例,展示了如何手动模拟一个类似切片扩容的过程(注意,这不是Go语言内部实现,仅用于理解概念):

package main

import (
    "fmt"
)

// 假设的底层数组类型
type array []int

// 自定义的切片类型
type mySlice struct {
    array  array
    length int
    cap    int
}

// 扩容函数
func (s *mySlice) grow(minCap int) {
    newCap := s.cap
    if newCap < minCap {
        if s.cap < 1024 {
            newCap = s.cap * 2
        } else {
            newCap += s.cap / 4 // 假设的增长策略,实际应用中会更复杂
        }
        if newCap < minCap {
            newCap = minCap
        }
    }

    // 简化处理,实际中需要处理内存分配等
    if newCap > len(s.array) {
        newArray := make(array, newCap)
        copy(newArray, s.array[:s.length])
        s.array = newArray
    }
    s.cap = newCap
}

// Append 模拟
func (s *mySlice) Append(value int) {
    if s.length == s.cap {
        s.grow(s.length + 1)
    }
    s.array[s.length] = value
    s.length++
}

func main() {
    slice := &mySlice{array: make(array, 0, 4), length: 0, cap: 4}
    slice.Append(1)
    slice.Append(2)
    slice.Append(3)
    slice.Append(4)
    slice.Append(5) // 触发扩容
    fmt.Println(slice.array)
}

请注意,上面的mySlice类型和grow方法仅用于说明目的,并不完全等同于Go语言标准库中的切片实现。在实际应用中,Go语言的切片扩容是自动且高效的,由Go的运行时环境处理。

回到顶部