Как предварительно распределить и заполнить кусочек указателей в go-idiomatic способом?
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)
}
Выходы: [<nil> <nil> <nil> <nil> <nil> 0xc010035160 0xc010035170 0xc010035180 0xc010035190 0xc0100351a0]
Что бы я хотел сделать, это предустановленная память для 5 бесполезных конструкций, хранящихся в виде указателей. Если я объявляю кусок структурных значений eq:
mySlice := make([]UselessStruct, 5)
то это создает 5 пустых структур - добавление не заменяет пустые структуры, а скорее продолжает добавление к срезу, поэтому конечный результат с этим кодом:
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)
}
: [{0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0} {0 0}]
Что такое go-idiomatic способ предварительного выделения и заполнения срезов?
Ответы
Ответ 1
Для вашего первого примера я бы сделал:
mySlice := make([]*UselessStruct, 5)
for i := range mySlice {
mySlice[i] = new(UselessStruct)
}
Проблема, с которой вы сталкиваетесь в обоих примерах, заключается в том, что вы добавляете к фрагменту уже правильную длину. Если вы устанавливаете mySlice := make([]*UselessStruct, 5)
, вы запрашиваете фрагмент из nil указателей длины 5. Если вы добавляете один указатель, он теперь имеет длину 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]
Оба моих примера будут работать, как вы ожидаете, но я рекомендую первый по чисто стилевым причинам.
Ответ 2
Есть два способа сделать это. Один из них заключается в предварительном распределении слотов, как вы это делали.
Но вместо использования 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)
инициализирует срез длиной 0, но он все еще предварительно выделяет достаточно места для 5 записей.
Ответ 3
Вы уверены, что вам нужны указатели? Ваша структура имеет нулевое значение, поэтому:
mySlice := make([]UselessStruct, 5) # has memory preallocated for 5 UselessStructs.
А поскольку срезы являются ссылочными типами, у вас фактически есть 5 указателей на те 5 бесполезных конструкций.
Если вам нужно получить ссылку на отдельную структуру, которая пройдет, тогда вы можете просто
myStruct := &mySlice[0]
И теперь у вас есть указатель на UseLessStruct для использования по своему усмотрению. Это гораздо меньше кода, чем у вас, и использует функцию нулевого значения.
Ответ 4
Просто для завершения: добавьте работы с 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{}
}
Это может избежать некоторого перераспределения.