
本文探讨了go语言中预分配并填充指针切片的两种惯用方法。针对 make([]*t, n) 后直接使用 append 导致切片中出现 nil 元素的问题,文章提供了两种解决方案:一是通过 make([]*t, n) 创建指定长度切片后,利用索引循环直接初始化每个元素;二是通过 make([]*t, 0, n) 创建零长度但预设容量的切片,然后使用 append 填充。这两种方法都能有效避免不必要的 nil 元素并提高性能。
在Go语言中,当我们需要一个特定类型的指针切片时,常见的做法是使用 make 函数进行预分配。然而,如果不正确地结合 make 和 append,可能会导致切片中出现意料之外的 nil 元素。
考虑以下示例,我们尝试预分配一个包含5个 UselessStruct 指针的切片:
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
mySlice := make([]*UselessStruct, 5) // 创建一个长度为5的指针切片
for i := 0; i != 5; i++ {
mySlice = append(mySlice, &UselessStruct{}) // 尝试追加新元素
}
fmt.Println(mySlice)
}上述代码的输出将是:[<nil> <nil> <nil> <nil> <nil> 0xc0... 0xc0... 0xc0... 0xc0... 0xc0...]。 这是因为 mySlice := make([]*UselessStruct, 5) 创建了一个长度为5的切片,其中所有元素都被初始化为零值。对于指针类型 *UselessStruct,其零值是 nil。此时切片的长度(len)为5,容量(cap)也为5。
随后,循环中的 append 操作并不会替换已有的 nil 元素,而是会在切片的末尾追加新的元素。由于切片的长度已经达到其容量,append 会导致底层数组重新分配,并将新元素添加到新的内存区域。最终,切片会包含最初的5个 nil 指针,以及后续追加的5个新结构体指针,总长度变为10。
类似地,如果切片存储的是结构体值而非指针,也会出现类似问题:
立即学习“go语言免费学习笔记(深入)”;
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
mySlice := make([]UselessStruct, 5) // 创建一个长度为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}]。这里,最初的5个元素是 UselessStruct 的零值 {0 0},后续追加的也是新的 {0 0} 结构体。
当您已知切片的最终长度,并且希望在创建时就填充所有元素时,最直接且惯用的方法是使用 make 创建指定长度的切片,然后通过索引来访问并初始化每个元素。
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
// 创建一个长度为5的指针切片,所有元素初始化为nil
mySlice := make([]*UselessStruct, 5)
// 遍历切片并为每个索引位置分配新的UselessStruct实例
for i := range mySlice {
mySlice[i] = new(UselessStruct) // 或者 mySlice[i] = &UselessStruct{}
}
fmt.Println(mySlice)
// 预期输出:[0xc0... 0xc0... 0xc0... 0xc0... 0xc0...] (5个不同的指针地址)
}在这个方法中:
这种方法简洁明了,适用于切片长度固定且所有元素都需要在创建后立即初始化的场景。
如果切片的最终长度在创建时未知,或者您希望逐步向切片中添加元素,同时又想避免频繁的内存重新分配以提高性能,那么可以使用 make 函数预设容量(capacity),并将切片初始长度设置为0。
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
// 创建一个长度为0,但容量为5的指针切片
mySlice := make([]*UselessStruct, 0, 5)
// 使用append追加元素,会利用预设的容量
for i := 0; i != 5; i++ {
mySlice = append(mySlice, &UselessStruct{})
}
fmt.Println(mySlice)
// 预期输出:[0xc0... 0xc0... 0xc0... 0xc0... 0xc0...] (5个不同的指针地址)
}在这个方法中:
这种方法适用于以下场景:
选择哪种方法取决于您的具体需求:
*直接索引填充 (`make([]T, N)+for i := range`)**:
*预设容量与 append (`make([]T, 0, N)+append`)**:
注意事项:
掌握这两种Go语言惯用的切片预分配和填充方法,将帮助您编写出更高效、更健壮的代码。
以上就是Go语言中预分配并填充指针切片的惯用方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号