如何在Go语言中实现可调整大小的数组

93

我来自C++背景,习惯使用std::vector类处理这种情况。 假设我想要一个动态数组:

type a struct {
    b int
    c string
}

如何标准地完成这个任务?

提供代码片段会非常有用


1
深入阅读:https://blog.golang.org/go-slices-usage-and-internals - user
7个回答

103
使用内置函数append()
示例:
type mytype struct {
  a, b int
}

func main() {
  a := []mytype{mytype{1, 2}, mytype{3, 4}}
  a = append(a, mytype{5, 6})
}

请参考规范获取有关append的更多信息。


还有一件事,cthmo06,如果可以的话:我正在使用“for _, t = range(msgs)”这个语句,其中msgs是一个向量。然后我必须将t转换为我的msg结构体。我能否在for语句中一行内完成所有操作? - user181351
@tm1rbrt 我不这么认为。至少使用range函数是不行的。 - cthom06
2
请注意,容器/向量包现已被删除。它已被普通切片和附加函数所取代。切片本质上是可调整大小的数组。 - Jeremy Wall
@JeremyWall 确实,我已经修复了它,因为它是被接受的答案。 - cthom06

76
一个 Go 切片包含三个元素:数据、长度和容量。
s := make([]int, 0, 10)

变量s是一个长度为0、容量为10的int类型切片。内置的len()和cap()函数可以让你获取切片的长度和容量:

len(s) == 0
cap(s) == 10

要增加切片的长度,只需重新切片:
s = s[0:5]
// len(s) == 5
// cap(s) == 10

为了缩短长度,您可以取一个子切片:
s = s[0:1]
// len(s) == 1

有一些更简短的方法来调用make()函数:
a := make([]int, 10) 
// len(a) == cap(a) == 10

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

这些都很好,但是如果您需要将切片的长度增加到超过其容量怎么办?为此,您需要分配一个新的切片并将旧切片的内容复制到新切片中。(内置函数“copy”也可以使用。)

t := make([]int, len(s), 20)
copy(t, s)
Effective Go文档进一步介绍了这个例子,实现了一个Append函数,将一个切片附加到另一个切片中,如果需要的话会调整大小。
切片由数组支持;当您使用make()创建具有特定容量的切片时,背景中分配了该容量的数组。切片有效地成为指向该数组的“智能指针”。如果您将该切片(或该切片的子切片)传递给另一个函数,则作为指向同一数组的指针传递。这使得子切片非常便宜 - 昂贵的是分配后面的数组。
Go标准库包括许多容器包 - 例如vector - 可消除手动管理切片的需要。使用切片来提高速度,使用更复杂的容器类来方便操作。(虽然如此,我仍然大多数情况下使用切片。)
您可能想知道为什么需要这么麻烦。毕竟,许多语言都提供动态调整大小的数组作为基元。原因与Go的哲学有关。语言设计者不假设他们知道程序的适当分配策略;相反,他们为您提供构建自己的数据结构所需的工具。

如果每次切片增长时都重新创建切片,将会耗费大量时间。然而,如果在切片超出其容量时每次将其容量加倍,则插入新元素到切片中可能不需要太多时间。例如:t := make([]int, 5, 10) u := make([]int, 10, 20)等等。 - Xeoncross
1
@Xeoncros,append 内置函数已经相当聪明地分配了额外的空间;只需使用它即可。 - Dave C
3
请注意,此部分内容已经过时: "将切片长度增加到超出其容量" ... "您需要分配一个新的切片,并将旧切片的内容复制到新切片中"。现在 append() 函数会进行重新分配。请参见 cthom06 和 Jessta 的回答。 - minghua

30

这个惯用的方法已经改变了。 由于内置的append()函数的添加,您可以像这样扩展一个切片:

type a struct {
    b int
    c string
}

func main(){
    var mySlice []a
    mySlice = append(mySlice,a{5,"pizza"})
}

如果切片有足够的空间,append()会将给定的项目附加到切片,否则它会扩展该切片。

有关append()的更多信息,请访问此处http://golang.org/doc/go_spec.html#Appending_and_copying_slices


5

一个更简单的 append() 内建函数示例

friends := []string{"Adam"}

friends = append(friends, "Rahul") // Add one friend or one string
        
friends = append(friends, "Angelica", "Rashi") // Add multiple friends or multiple strings

append()文档在这里


2
你也可以使用切片(slice)来满足需求,它是一个数组,知道自己当前的长度,并且可以有单独的当前长度和最大容量。需要注意的是,传递给初始大小和容量的值不必是常量,因此您可以创建一个函数,根据其参数构建并返回不同长度的切片。
优点是,[]Int类型的切片可以像数组一样进行索引,并在此方式下返回整数。
缺点是,它不会自动增长超过其声明的容量。Effective Go中有一个示例,说明如何处理重新分配。
代码如下:
type mytype struct {
   a, b int
}




func main() {

  sl := make([]mytype, 10, 50) //slice of 10 items, max capacity 50 these do not have to be constant expressions.
  sl[0] = mytype{1,2}
   //...
  for i, value := range sl {
  // ... do stuff with value
  }
}

0

嗨,我们可以用两种简单的方法来做到这一点

type mytype struct {
  a, b int
}

就像这样做

  1. 不要附加

__

a := []mytype{mytype{1, 2}, mytype{3, 4}, mytype{4, 5}}
  • 使用append方法
  • __

    a:=  append([]mytype{}, mytype{1, 2}, mytype{3, 4}, mytype{4, 5})
    

    添加任意数量的内容。第一个方法是实现这一功能的简单方式。希望这能对您有所帮助。


    我不确定为什么这个被投票否决了。这是对被接受答案的改进。 - user12817546

    0

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接