Golang语言中切片的长度和容量的概念和使用

作者:

文章目录
  • 长度(Length): 表示切片当前实际包含的元素个数。 通过内置函数 len(slice) 获取。 访问切片中索引 >= len(slice) 的元素会引发“索引越界”的运行时恐慌(panic)。 容量(Capacity): 表示切片底层数组(Underlying Array)从切片的起始位置到数组末尾的元素个数。 它代表了切片在不分配新内存的情况下,可以增长的最大限度。 通过内置函数 cap(slice) 获取。
  • 切片是一个轻量级的数据结构,它提供了对底层数组一个连续片段的引用。它包含三个组件: 指针:指向底层数组中切片起始位置的元素。 长度:切片中元素的数量。 容量:从切片起始位置到底层数组末尾的元素数量。 这个概念是理解长度和容量区别的关键。
  • 让我们通过一个例子和图示来理解。 // 1. 创建一个底层数组 arr := [5]int{10, 20, 30, 40, 50} // 数组,长度和容量固定为5 // 2. 基于数组创建一个切片 // slice 从 arr[1] 开始,到 arr[3] 结束 (不包括 arr[4]) slice := arr[1:4] // [20, 30, 40] 此时的内存布局可以这样表示: 底层数组: [10, 20, 30, 40, 50] 索引: 0 1 2 3 4 ^ ^ | | slice起始 slice结束 (不包括索引4) slice指针指向这里 len(slice):切片包含了 arr[1], arr[2], arr[3] 这三个元素,所以 长度为 3。 cap(slice):切片的指针从 arr[1] 开始,底层数组的末尾是 arr[4],所以从起始位置到末尾有 arr[1], arr[2], arr[3], arr[4] 这 4个位置,因此 容量为 4。 fmt.Println(slice) // 输出: [20 30 40] fmt.Println(len(slice)) // 输出: 3 fmt.Println(cap(slice)) // 输出: 4
  • 当你使用 append 函数向切片追加新元素时,Go 运行时会检查容量是否足够。 容量足够时:新元素会被直接放入底层数组切片末尾之后的空间,长度增加,容量不变。 newSlice := append(slice, 60) // 追加元素 60 fmt.Println(newSlice) // 输出: [20 30 40 60] fmt.Println(len(newSlice)) // 输出: 4 fmt.Println(cap(newSlice)) // 输出: 4 (容量刚好够用,没有分配新数组) fmt.Println(arr) // 输出: [10 20 30 40 60] (原数组被修改了!) 容量不足时:Go 运行时会创建一个新的、更大的底层数组(通常是当前容量的 2 倍,但对于较小的切片,增长策略可能不同),将原有元素复制到新数组中,然后追加新元素。此时的切片引用的是一个全新的数组,与原数组无关。 newSlice2 := append(newSlice, 70, 80) // 追加两个元素,超出当前容量4 fmt.Println(newSlice2) // 输出: [20 30 40 60 70 80] fmt.Println(len(newSlice2)) // 输出: 6 fmt.Println(cap(newSlice2)) // 输出: 8 (新数组的容量,通常是旧容量的2倍) fmt.Println(arr) // 输出: [10 20 30 40 60] (原数组未受影响)
  • 你可以使用 make 函数在创建切片时直接指定其初始长度和容量。 语法:make([]T, length, capacity) // 创建一个切片,长度为3(前3个元素被初始化为零值),容量为5 s := make([]int, 3, 5) fmt.Println(s) // 输出: [0 0 0] fmt.Println(len(s)) // 输出: 3 fmt.Println(cap(s)) // 输出: 5 // 你可以安全地访问和修改 s[0], s[1], s[2] // 你可以追加最多2个新元素(5-3=2)而不会触发重新分配 s = append(s, 1) fmt.Println(s) // 输出: [0 0 0 1] fmt.Println(len(s)) // 输出: 4 如果省略容量参数,则容量默认与长度相等。 s2 := make([]int, 3) // len=3, cap=3 fmt.Println(s2, len(s2), cap(s2)) // 输出: [0 0 0] 3 3
  • 特性 长度 (len) 容量 (cap) 含义 当前元素个数 可增长的最大限度 函数 len(s) cap(s) 动态性 随 append/slice 操作变化 在触发扩容前不变 用途 决定可访问的元素范围 影响性能(减少内存分配和复制) 切片是引用类型。多个切片可以共享同一个底层数组。修改一个切片的元素可能会影响其他共享底层数组的切片。 “扩容”是一个相对昂贵的操作,涉及内存分配和数据复制。在能预估所需数据量的情况下,使用 make([]T, 0, capacity) 预先分配一个足够大的容量是提升性能的有效手段。 对切片进行重新切片(reslicing,如 s2 := s1[1:3])操作时,新切片会和原切片共享底层数组。新切片的长度和容量会基于新的起始索引重新计算。 理解长度和容量的区别,能帮助你更好地使用切片,写出更高效、更可靠的 Go 代码。 到此这篇关于Golang语言中切片的长度和容量的概念和使用的文章就介绍到这了,更多相关Golang 切片的长度和容量内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Golang中切片长度和容量的区别示例详解
  • 目录
    • 核心概念
    • 底层数组(Underlying Array)
    • 图示与示例
    • 容量是如何被使用的:append 函数
    • 使用make创建切片时指定长度和容量
    • 总结与要点

    本文我们来详细讲解 Go 语言中切片(Slice)的长度(Length)容量(Capacity)。这是理解切片工作原理的核心概念。

    1. 长度(Length):

      • 表示切片当前实际包含的元素个数
      • 通过内置函数 len(slice) 获取。
      • 访问切片中索引 >= len(slice) 的元素会引发“索引越界”的运行时恐慌(panic)。
    2. 容量(Capacity):

      • 表示切片底层数组(Underlying Array)从切片的起始位置到数组末尾的元素个数
      • 它代表了切片在不分配新内存的情况下,可以增长的最大限度。
      • 通过内置函数 cap(slice) 获取。

    切片是一个轻量级的数据结构,它提供了对底层数组一个连续片段的引用。它包含三个组件:

    • 指针:指向底层数组中切片起始位置的元素。
    • 长度:切片中元素的数量。
    • 容量:从切片起始位置到底层数组末尾的元素数量。

    这个概念是理解长度和容量区别的关键。

    让我们通过一个例子和图示来理解。

    // 1. 创建一个底层数组
    arr := [5]int{10, 20, 30, 40, 50} // 数组,长度和容量固定为5
    
    // 2. 基于数组创建一个切片
    // slice 从 arr[1] 开始,到 arr[3] 结束 (不包括 arr[4])
    slice := arr[1:4] // [20, 30, 40]
    

    此时的内存布局可以这样表示:

    底层数组: [10, 20, 30, 40, 50]
    索引:      0   1   2   3   4
                  ^         ^
                  |         |
               slice起始   slice结束 (不包括索引4)
               slice指针指向这里
    
    • len(slice):切片包含了 arr[1], arr[2], arr[3] 这三个元素,所以 长度为 3。
    • cap(slice):切片的指针从 arr[1] 开始,底层数组的末尾是 arr[4],所以从起始位置到末尾有 arr[1], arr[2], arr[3], arr[4] 这 4个位置,因此 容量为 4。
    fmt.Println(slice)      // 输出: [20 30 40]
    fmt.Println(len(slice)) // 输出: 3
    fmt.Println(cap(slice)) // 输出: 4
    

    当你使用 append 函数向切片追加新元素时,Go 运行时会检查容量是否足够。

    1. 容量足够时
      新元素会被直接放入底层数组切片末尾之后的空间,长度增加,容量不变。

      newSlice := append(slice, 60) // 追加元素 60
      fmt.Println(newSlice)          // 输出: [20 30 40 60]
      fmt.Println(len(newSlice))     // 输出: 4
      fmt.Println(cap(newSlice))     // 输出: 4 (容量刚好够用,没有分配新数组)
      fmt.Println(arr)               // 输出: [10 20 30 40 60] (原数组被修改了!)
      
    2. 容量不足时
      Go 运行时会创建一个新的、更大的底层数组(通常是当前容量的 2 倍,但对于较小的切片,增长策略可能不同),将原有元素复制到新数组中,然后追加新元素。此时的切片引用的是一个全新的数组,与原数组无关

      newSlice2 := append(newSlice, 70, 80) // 追加两个元素,超出当前容量4
      fmt.Println(newSlice2)                 // 输出: [20 30 40 60 70 80]
      fmt.Println(len(newSlice2))            // 输出: 6
      fmt.Println(cap(newSlice2))            // 输出: 8 (新数组的容量,通常是旧容量的2倍)
      fmt.Println(arr)                       // 输出: [10 20 30 40 60] (原数组未受影响)
      

    你可以使用 make 函数在创建切片时直接指定其初始长度和容量。

    语法:make([]T, length, capacity)

    // 创建一个切片,长度为3(前3个元素被初始化为零值),容量为5
    s := make([]int, 3, 5)
    fmt.Println(s)          // 输出: [0 0 0]
    fmt.Println(len(s))     // 输出: 3
    fmt.Println(cap(s))     // 输出: 5
    
    // 你可以安全地访问和修改 s[0], s[1], s[2]
    // 你可以追加最多2个新元素(5-3=2)而不会触发重新分配
    s = append(s, 1)
    fmt.Println(s)          // 输出: [0 0 0 1]
    fmt.Println(len(s))     // 输出: 4
    

    如果省略容量参数,则容量默认与长度相等。

    s2 := make([]int, 3) // len=3, cap=3
    fmt.Println(s2, len(s2), cap(s2)) // 输出: [0 0 0] 3 3
    

    特性 长度 (len) 容量 (cap)
    含义 当前元素个数 可增长的最大限度
    函数 len(s) cap(s)
    动态性 随 append/slice 操作变化 在触发扩容前不变
    用途 决定可访问的元素范围 影响性能(减少内存分配和复制)
    • 切片是引用类型。多个切片可以共享同一个底层数组。修改一个切片的元素可能会影响其他共享底层数组的切片。
    • “扩容”是一个相对昂贵的操作,涉及内存分配和数据复制。在能预估所需数据量的情况下,使用 make([]T, 0, capacity) 预先分配一个足够大的容量是提升性能的有效手段。
    • 对切片进行重新切片(reslicing,如 s2 := s1[1:3])操作时,新切片会和原切片共享底层数组。新切片的长度和容量会基于新的起始索引重新计算。

    理解长度和容量的区别,能帮助你更好地使用切片,写出更高效、更可靠的 Go 代码。

    到此这篇关于Golang语言中切片的长度和容量的概念和使用的文章就介绍到这了,更多相关Golang 切片的长度和容量内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • Golang中切片长度和容量的区别示例详解

    站内搜索