在 Go 中,make 是一个内置函数,用于创建和初始化以下三种引用类型的数据结构:
- 切片(Slice)
- 映射(Map)
- 通道(Channel)
make 的主要作用是分配内存并初始化这些数据结构,使它们可以正常使用。以下是 make 的使用场景和具体示例。
1. 创建切片(Slice)
场景
- 当需要动态分配一个切片,并指定其初始长度和容量时使用。
- 切片的底层需要一个数组,
make会自动为切片分配底层数组的内存。
语法
make([]T, length, capacity)
-
T:切片的元素类型。 -
length:切片的初始长度(即切片中有多少个元素可用)。 -
capacity(可选):切片的容量(底层数组的大小)。如果未指定,容量默认为长度。
示例
package main
import "fmt"
func main() {
// 创建一个长度为 3,容量为 5 的切片
s := make([]int, 3, 5)
fmt.Println(s) // 输出:[0 0 0],初始值为零值
fmt.Println(len(s), cap(s)) // 输出:3 5
// 修改切片元素
s[0] = 10
fmt.Println(s) // 输出:[10 0 0]
// 动态扩展切片
s = append(s, 20, 30)
fmt.Println(s) // 输出:[10 0 0 20 30]
fmt.Println(len(s), cap(s)) // 输出:5 5
// 再次扩展,超过容量,触发扩容
s = append(s, 40)
fmt.Println(s) // 输出:[10 0 0 20 30 40]
fmt.Println(len(s), cap(s)) // 输出:6 10(容量通常翻倍)
}
2. 创建映射(Map)
场景
- 当需要创建一个空的映射(键值对集合)时使用。
-
make会初始化映射的内部数据结构,使其可以用于存储键值对。
语法
make(map[K]V, hint)
-
K:键的类型。 -
V:值的类型。 -
hint(可选):一个容量提示,用于告诉 Go 预分配多少空间(不是固定大小,映射会根据需要动态扩展)。
示例
package main
import "fmt"
func main() {
// 创建一个空的映射
m := make(map[string]int)
fmt.Println(m) // 输出:map[]
// 添加键值对
m["Alice"] = 25
m["Bob"] = 30
fmt.Println(m) // 输出:map[Alice:25 Bob:30]
// 访问键值对
fmt.Println(m["Alice"]) // 输出:25
// 检查键是否存在
value, exists := m["Charlie"]
fmt.Println(value, exists) // 输出:0 false
// 删除键
delete(m, "Bob")
fmt.Println(m) // 输出:map[Alice:25]
}
3. 创建通道(Channel)
场景
- 当需要创建一个通道用于协程之间的通信时使用。
-
make会初始化通道的内部数据结构,使其可以正常传递数据。
语法
make(chan T, bufferSize)
-
T:通道中传递的数据类型。 -
bufferSize(可选):通道的缓冲区大小。- 如果
bufferSize为 0 或未指定,则创建的是无缓冲通道。 - 如果
bufferSize大于 0,则创建的是有缓冲通道。
- 如果
示例
package main
import "fmt"
func main() {
// 创建一个无缓冲通道
ch := make(chan int)
// 使用 goroutine 发送数据
go func() {
ch <- 42
}()
// 接收数据
value := <-ch
fmt.Println(value) // 输出:42
// 创建一个有缓冲通道
chBuffered := make(chan int, 2)
chBuffered <- 10
chBuffered <- 20
fmt.Println(<-chBuffered) // 输出:10
fmt.Println(<-chBuffered) // 输出:20
}
4. 与 new 的区别
Go 中除了 make,还有另一个内置函数 new,它们的作用不同:
| 特性 | make |
new |
|---|---|---|
| 适用类型 | 仅适用于切片(Slice)、映射(Map)、通道(Channel) | 适用于所有类型 |
| 返回值 | 返回初始化后的引用类型(非指针) | 返回指向零值的指针 |
| 主要用途 | 分配内存并初始化内部数据结构 | 仅分配内存,不初始化 |
示例:
package main
import "fmt"
func main() {
// 使用 make 创建切片
s := make([]int, 3)
fmt.Println(s) // 输出:[0 0 0]
// 使用 new 创建数组指针
p := new([3]int)
fmt.Println(*p) // 输出:[0 0 0]
}
5. 使用场景总结
切片(Slice)
- 需要创建一个动态长度的数据集合。
- 需要指定切片的初始长度和容量。
- 需要对底层数组进行动态扩展时。
映射(Map)
- 需要存储键值对数据。
- 需要高效的键值查找、插入和删除操作。
- 需要初始化一个空的映射。
通道(Channel)
- 需要在多个 Goroutine 之间传递数据。
- 需要创建无缓冲或有缓冲的通信通道。
6. 注意事项
-
不能用
make创建其他类型:-
make只能用于切片、映射和通道,不能用于数组、结构体等其他类型。 - 对于其他类型的初始化,通常使用
new或直接字面量赋值。
-
-
切片的容量提示:
- 使用
make([]T, len, cap)时,cap的设置可以减少切片扩容的次数,从而提高性能。
- 使用
-
映射的容量提示:
-
make(map[K]V, hint)中的hint只是容量的提示,Go 不会强制遵守该值。
-
-
通道的缓冲区大小:
- 无缓冲通道会导致发送和接收操作阻塞,适用于严格同步的场景。
- 有缓冲通道允许一定数量的数据缓冲,适用于异步通信场景。
总结
make 是 Go 中用于创建和初始化切片、映射和通道的关键工具。它的使用场景非常明确:
- 切片:动态数组,支持扩展。
- 映射:键值对存储。
- 通道:协程间通信。
通过合理使用 make,可以高效地管理内存并初始化复杂的数据结构。








网友评论