
本文深入探讨了在go语言中如何以惯用的方式预分配和填充包含指针的切片。通过分析`make`函数对切片长度和容量的影响,以及`append`操作的行为,文章指出了常见的误区。我们提供了两种主要的解决方案:一是通过直接索引来填充已预设长度的切片,二是利用`make`函数预设容量并结合`append`操作来高效构建切片。
在Go语言中,切片(slice)是构建动态数组的强大工具。然而,当涉及到预分配并填充包含指针的切片时,如果不理解make函数和append操作的底层机制,可能会导致非预期的结果。本文将详细阐述Go语言中预分配和填充切片的惯用方法。
在Go中,make函数用于创建切片、映射或通道。对于切片,它有以下两种常用形式:
append函数用于向切片追加元素。它总是将新元素添加到切片的末尾,并返回一个新的切片(如果底层数组需要重新分配)。append操作会增加切片的长度,但不会改变已存在元素的索引或值。
考虑以下代码,尝试预分配一个包含5个*UselessStruct指针的切片,然后使用append填充:
立即学习“go语言免费学习笔记(深入)”;
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
mySlice := make([]*UselessStruct, 5) // 长度为5,元素为5个nil指针
for i := 0; i != 5; i++ {
mySlice = append(mySlice, &UselessStruct{}) // 追加新元素
}
fmt.Println(mySlice)
}这段代码的输出是:[<nil> <nil> <nil> <nil> <nil> 0xc0... 0xc0... 0xc0... 0xc0... 0xc0...]。 可以看到,前5个元素仍然是nil,append操作并没有替换这些nil值,而是将新的UselessStruct指针追加到了切片的末尾,导致切片的长度变成了10。
如果切片存储的是结构体值而非指针,情况类似:
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
mySlice := make([]UselessStruct, 5) // 长度为5,元素为5个零值UselessStruct
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个元素是零值结构体,后续追加的才是新的结构体。
这表明,当使用make([]Type, length)创建一个具有非零长度的切片时,切片已经包含了length个元素(零值)。append操作只会在此基础上增加新元素,而不会覆盖已存在的元素。
Go语言提供了两种惯用的方法来正确地预分配和填充切片,具体取决于你的需求:
当你事先知道切片的最终长度,并且希望填充所有元素时,最直接和惯用的方法是使用make([]Type, length)创建切片,然后通过索引直接为每个位置赋值。
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
const size = 5
mySlice := make([]*UselessStruct, size) // 创建一个长度为5的切片,元素为nil指针
// 通过索引直接填充每个位置
for i := range mySlice {
mySlice[i] = new(UselessStruct) // 为每个位置分配并赋值一个新的UselessStruct指针
// 或者使用 &UselessStruct{} 也可以
// mySlice[i] = &UselessStruct{}
}
fmt.Println(mySlice)
// 预期输出:[0xc0... 0xc0... 0xc0... 0xc0... 0xc0...] (5个不同的指针)
}在这个例子中:
这种方法简洁明了,直接表达了“我需要一个固定大小的切片,并用特定值填充它”的意图。
当你需要动态地向切片添加元素,但又希望通过预分配内存来避免频繁的底层数组重新分配(这会带来性能开销)时,可以使用make([]Type, 0, capacity)来创建切片。这会创建一个长度为0但预留了capacity空间(底层数组)的切片。
package main
import "fmt"
type UselessStruct struct {
a int
b int
}
func main() {
const capacity = 5
// 创建一个长度为0,但容量为5的切片
mySlice := make([]*UselessStruct, 0, capacity)
// 使用append追加元素
for i := 0; i < capacity; i++ {
mySlice = append(mySlice, &UselessStruct{}) // 追加新的UselessStruct指针
}
fmt.Println(mySlice)
// 预期输出:[0xc0... 0xc0... 0xc0... 0xc0... 0xc0...] (5个不同的指针)
}在这个例子中:
这种方法适用于你不知道最终长度,但可以预估一个大致容量的场景,以优化性能。
理解这些基本概念是编写高效、惯用Go代码的关键。选择哪种方法取决于你的具体需求:如果长度固定且已知,直接索引填充更简洁;如果长度动态且需要性能优化,预设容量并结合append是更好的选择。
以上就是Go语言中切片指针的预分配与填充:惯用方法解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号