如何以Go语言的惯用方式预分配并填充一个指针切片?

huangapple go评论82阅读模式
英文:

How to preallocate and fill a slice of pointers in a go-idiomatic way?

问题

package main

import "fmt"

type UselessStruct struct {
a int
b int
}

func main() {
mySlice := make([]*UselessStruct, 5)
for i := 0; i != 5; i++ {
mySlice = append(mySlice, &UselessStruct{})
}

fmt.Println(mySlice)

}

英文:

http://play.golang.org/p/j-Y0mQzTdP

package main

import "fmt"

type UselessStruct struct {
	a int
	b int
}

func main() {
	mySlice := make([]*UselessStruct, 5)
	for i := 0; i != 5; i++ {
		mySlice = append(mySlice, &UselessStruct{})
	}

	fmt.Println(mySlice)
}

Outputs: [<nil> <nil> <nil> <nil> <nil> 0xc010035160 0xc010035170 0xc010035180 0xc010035190 0xc0100351a0]

What i would like to do, is preallocate memory for 5 UselessStructs, stored as pointers. If i declare a slice of struct values eq:

mySlice := make([]UselessStruct, 5)

then this creates 5 empty structs - appending doesn't replace the empty structs, but rather keeps on adding to the slice, so the end result with this code:

http://play.golang.org/p/zBYqGVO85h

package main

import "fmt"

type UselessStruct struct {
	a int
	b int
}

func main() {
	mySlice := make([]UselessStruct, 5)
	for i := 0; i != 5; i++ {
		mySlice = append(mySlice, UselessStruct{})
	}

	fmt.Println(mySlice)
}

is: [{0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0}]

What is the the go-idiomatic way to preallocate and fill slices?

答案1

得分: 53

对于你的第一个例子,我会这样做:

mySlice := make([]*UselessStruct, 5)
for i := range mySlice {
     mySlice[i] = new(UselessStruct)
}

你在这两个例子中遇到的问题是你在已经是正确长度的切片上进行了追加。如果你设置 mySlice := make([]*UselessStruct, 5),你要求一个长度为5的nil指针切片。如果你追加一个指针,它现在的长度就变成了6。

相反,你应该使用 mySlice := make([]*UselessStruct, 0, 5)。这将创建一个长度为0但容量为5的切片。每次追加时,它会将长度加一,但在超过切片容量之前不会重新分配内存。

mySlice := make([]*UselessStruct, 0, 5)
for i := 0; i != 5; i++ {
    mySlice = append(mySlice, &UselessStruct{})
}
// mySlice is [0xc010035160 0xc010035170 0xc010035180 0xc010035190 0xc0100351a0]

我给出的两个例子都会按照你的期望工作,但出于纯粹的风格原因,我推荐使用第一个例子。

英文:

For your first example, I would do:

mySlice := make([]*UselessStruct, 5)
for i := range mySlice {
     mySlice[i] = new(UselessStruct)
}

The issue you are facing in both examples is you are appending to a slice that is already the correct length. If you set mySlice := make([]*UselessStruct, 5), you are asking for a slice of nil pointers of length 5. If you append one pointer, it now has length 6.

Instead, you want to use mySlice := make([]*UselessStruct, 0, 5). This creates a slice of length 0 but capacity 5. Each time you append it will add one to the length but it won't reallocate until you exceed the slice's capacity.

mySlice := make([]*UselessStruct, 0, 5)
for i := 0; i != 5; i++ {
    mySlice = append(mySlice, &UselessStruct{})
}
// mySlice is [0xc010035160 0xc010035170 0xc010035180 0xc010035190 0xc0100351a0]

Both of my examples will work as you expect but I recommend the first one for purely style reasons.

答案2

得分: 9

有两种方法可以做到这一点。一种是像你所做的那样预先分配槽位。但是,不要使用append,而是直接索引到现有的槽位中:

mySlice[i] = &UselessStruct{}

第二种方法是使用“重载”的make版本。你指定长度为零,但容量为5。

package main

type T struct {
    A int
    B int
}

func main() {
    mySlice := make([]*T, 0, 5)
    for i := 0; i < 5; i++ {
        mySlice = append(mySlice, &T{1, 2})
    }
}

mySlice := make([]*T, 0, 5)初始化了长度为零的切片,但仍然预先分配了足够的空间来存储5个条目。

英文:

There are two ways to do this. One is by pre-allocating the slots as you did.
But instead of using append, you simply index into one of the existing slots:

mySlice[i] = &amp;UselessStruct{}

The second is to use the 'overloaded' verion of make. You specify zero length, but a capacity of 5.

package main

type T struct {
    A int
    B int
}

func main() {
    mySlice := make([]*T, 0, 5)
    for i := 0; i &lt; 5; i++ {
        mySlice = append(mySlice, &amp;T{1, 2})
    }
}

mySlice := make([]*T, 0, 5) initializes the slice with a length of zero, but it still pre-allocates enough space for 5 entries.

答案3

得分: 4

你确定你需要指针吗?你的结构体有一个零值,所以:

mySlice := make([]UselessStruct, 5) // 预分配了5个UselessStruct的内存。

而且,由于切片是引用类型,你实际上有5个指向这5个UselessStruct的指针。

如果你需要获取一个单独结构体的引用来传递,你可以这样做:

myStruct := &mySlice[0]

现在你有一个指向UselessStruct的指针,可以根据需要使用它。这比你的代码少得多,并且利用了Go的零值特性。

英文:

Are you sure you need pointers? Your struct has a zero value so:

mySlice := make([]UselessStruct, 5) // has memory preallocated for 5 UselessStructs.

And since slices are reference types you effectively have 5 pointers to those 5 UselessStructs.

If you need to get a reference to an individual struct to pass around then you can just so

myStruct := &amp;mySlice[0]

And now you have a pointer to a UselessStruct to use as you see fit. It's far less code than you have and leverages Go's zero value feature.

答案4

得分: 1

只需补充一下:append函数可以与nil切片一起使用,所以你不需要使用make创建切片,只需将元素追加到其中即可。

var mySlice []*UselessStruct
for i := 0; i < 5; i++ {
    mySlice = append(mySlice, &UselessStruct{})
}

这将与之前的示例相同,但不需要预先分配空间,但如果你知道大小,最好使用类似这样的方式:

mySlice := make([]*UselessStruct, 0, 5)
for i := range mySlice {
    mySlice[i] = &UselessStruct{}
}

这样可以避免一些重新分配的情况。

英文:

Just to complete: append works with nil slice so, you don't need to create the slice with make, you can just append element to it.

var mySlice []*UselessStruct
for i := 0; i &lt; 5; i++ {
    mySlice = append(mySlice, &amp;UselessStruct{})
}

This will do the same as previous example without pre-allocation, but if you know the size, you rather use something like this one:

mySlice := make([]*UselessStruct, 0, 5)
for i := range mySlice {
    mySlice[i] = &amp;UselessStruct{}
}

This may avoid some reallocation.

huangapple
  • 本文由 发表于 2013年6月4日 05:26:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/16906113.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定