golang中切片的cap vs len

Posted

技术标签:

【中文标题】golang中切片的cap vs len【英文标题】:cap vs len of slice in golang 【发布时间】:2017-05-30 19:11:18 【问题描述】:

golang中切片的cap和len有什么区别?

根据定义:

切片既有长度又有容量。

切片的长度是它包含的元素数。

切片的容量是底层数组中元素的数量,从切片中的第一个元素开始计数。

x := make([]int, 0, 5) // len(b)=0, cap(b)=5

len 是否仅表示非空值?

【问题讨论】:

【参考方案1】:

切片是在底层使用数组的抽象。

cap 告诉你底层数组的容量。 len 告诉您数组中有多少项。

Go 中的切片抽象非常好,因为它会为您调整底层数组的大小,而且在 Go 中数组无法调整大小,因此几乎总是使用切片来代替。

例子:

s := make([]int, 0, 3)
for i := 0; i < 5; i++ 
    s = append(s, i)
    fmt.Printf("cap %v, len %v, %p\n", cap(s), len(s), s)

会输出如下内容:

cap 3, len 1, 0x1040e130
cap 3, len 2, 0x1040e130
cap 3, len 3, 0x1040e130
cap 6, len 4, 0x10432220
cap 6, len 5, 0x10432220

如您所见,一旦满足容量,append 将返回一个容量更大的新切片。在第 4 次迭代中,您会注意到更大的容量和新的指针地址。

Play example

我知道您没有询问数组和追加,但它们对于理解切片和内置函数的原因非常基础。

【讨论】:

很好的答案。 OP 可能不再关心了,但我可以看到这对很多可能会在路上找到它的人有所帮助。 @RandyHoward 谢谢,我很感激。我发现知道指针地址的变化非常重要。 @jmaloney。这很有帮助。我仍然难以理解的一件事是数组和切片的 nil 值为零(不是 nil 或 null),所以长度实际上如何与容量不同。容量为 3 的切片中的每个元素不应该在第一次迭代中具有 1,0,0 ......让 len 为 3 而不是 1?我的意思是,如果这只是约定,我不理解约定中的逻辑。我只是错过了什么还是应该接受它? @BradEllis 数组与切片不同,没有 nil 值。当您创建数组var array [3]int 时,您明确分配了数组的容量和长度。当谈到 Go 中的数组时,cap 和 len 函数是相同的。您需要记住,Go 中的切片是一种使用数组但不是数组的数据类型。因此,当您调用 cap(someSlice) 时,您正在检查下划线数组的容量或长度,但是当您调用 len(someSlice) 时,您正在检查明确添加到切片中的项目数。示例:play.golang.org/p/ahlHLkD4_Wt go 在达到上限时将cap 加倍.. 为什么?这是一件好事吗...例如添加769 项目数组上限将为1536【参考方案2】:

来自source code:

// The len built-in function returns the length of v, according to its type:
//  Array: the number of elements in v.
//  Pointer to array: the number of elements in *v (even if v is nil).
//  Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
//  String: the number of bytes in v.
//  Channel: the number of elements queued (unread) in the channel buffer;
//  if v is nil, len(v) is zero.
func len(v Type) int

// The cap built-in function returns the capacity of v, according to its type:
//  Array: the number of elements in v (same as len(v)).
//  Pointer to array: the number of elements in *v (same as len(v)).
//  Slice: the maximum length the slice can reach when resliced;
//  if v is nil, cap(v) is zero.
//  Channel: the channel buffer capacity, in units of elements;
//  if v is nil, cap(v) is zero.
func cap(v Type) int

【讨论】:

【参考方案3】:

简单的解释 切片是自增长的数组形式,因此有两个主要属性。

长度是 slice 所拥有的 elements() 总数,可用于遍历我们存储在 slice 中的元素。同样,当我们打印切片时,所有元素都被打印出来。

容量是底层数组中完全没有元素,当您添加更多元素时,长度会增加直到容量。之后,对切片的任何进一步追加都会导致容量自动增加(apprx 双倍)和长度增加没有追加的元素。

真正的魔法发生在你从一个切片中切出子切片时,所有实际的读/写都发生在底层数组上。因此,子切片的任何更改也会更改原始切片和底层数组中的数据。任何子切片都可以有自己的长度和容量。

仔细阅读下面的程序。其修改版的golang游览示例

package main

import "fmt"

func main() 
    sorig := []int2, 3, 5, 7, 11, 13
    printSlice(sorig)

    // Slice the slice to give it zero length.
    s := sorig[:0]
    printSlice(s)

    // Extend its length.
    s = s[:4]
    s[2] = 555
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
    
    printSlice(sorig)


func printSlice(s []int) 
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)

//Output
//len=6 cap=6 [2 3 5 7 11 13]
//len=0 cap=6 []
//len=4 cap=6 [2 3 555 7]
//len=2 cap=4 [555 7]
//len=6 cap=6 [2 3 555 7 11 13]

【讨论】:

【参考方案4】:

您已经回答了您的问题 - 切片中基础数组的长度不一定与数组包含的元素数相同。

【讨论】:

以上是关于golang中切片的cap vs len的主要内容,如果未能解决你的问题,请参考以下文章

Golang 切片

golang切片类型

golang之切片

go语言中实现切片(slice)的三种方式

Go 语言中的 nil 切片 vs 非 nil 切片 vs 空切片

golang 数据二 (切片)