Go 语言make函数
在 Go 语言中,make
函数是用于初始化引用类型(切片 slice、映射 map 和通道 channel)的内置函数。以下是需要用到 make
函数的典型场景:
1. 创建切片(Slice)
当需要指定长度和容量时,必须用 make
:
// 创建长度为 3,容量为 5 的 int 切片
s := make([]int, 3, 5)
- 直接声明
var s []int
得到的是nil
切片 make
会分配底层数组内存并初始化零值
2. 创建映射(Map)
所有 map 必须用 make
初始化才能使用:
// 正确:初始化空 map
m := make(map[string]int)// 错误:未初始化的 nil map 会 panic!
var nilMap map[string]int
nilMap["key"] = 42 // panic: assignment to nil map
- 可选指定初始容量(提升性能):
m := make(map[int]string, 100) // 预分配 100 个键值对的空间
意义相同写法不同:
// 使用字面量初始化(直接包含初始键值对)
m := map[string]int{"Alice": 30,"Bob": 25,
}// 等价于使用 make + 赋值
m := make(map[string]int)
m["Alice"] = 30
m["Bob"] = 25
关键区别在于:
- 隐式内存分配:字面量语法会自动调用
make
进行内存分配 - 初始化即赋值:适合在声明时已知具体键值对的情况
- 代码简洁性:省去了显式的
make
调用和后续的多次赋值操作
只有当需要创建空映射(没有初始元素)或需要指定初始容量时,才需要显式使用 make
:
// 需要显式 make 的情况
emptyMap := make(map[string]int) // 空映射
preAllocatedMap := make(map[string]int, 10) // 预分配容量
3. 创建通道(Channel)
初始化通道时必须用 make
:
// 无缓冲通道
ch1 := make(chan int)// 带缓冲通道(容量为 10)
ch2 := make(chan string, 10)
4. 需要预分配空间时
使用 make
预分配内存可避免频繁扩容,提升性能:
// 预分配 1w 个元素的切片(避免 append 时多次扩容)
data := make([]int, 0, 10_000)// 预分配大容量 map
cache := make(map[int]float64, 5000)
关键区别:make
vs new
特性 | make | new |
---|---|---|
适用类型 | slice, map, channel | 任意类型(如 struct, int 等) |
返回值 | 初始化后的引用类型 | 指向类型的指针 (*T ) |
内存初始化 | 分配内存并初始化(如 slice 的零值填充) | 分配零值内存,返回指针 |
示例 | m := make(map[int]bool) | p := new(int) → *p = 10 |
何时用 make
?总结
- 创建非零值的 slice/map/channel
- 需要预分配内存容量时
- 避免 nil 引用导致 panic 时:
// 安全用法m := make(map[string]int)m["safe"] = 1 // 正常
典型示例场景
func main() {// 场景1:需要初始化后才能操作的 mapconfig := make(map[string]string)config["env"] = "production"// 场景2:预分配大切片data := make([]int, 0, 1e6) // 100万容量for i := 0; i < 1e6; i++ {data = append(data, i) // 避免多次扩容}// 场景3:创建通道ch := make(chan os.Signal, 1)signal.Notify(ch, syscall.SIGINT)
}
📌 经验法则: 当处理 slice/map/channel 时,优先考虑
make
——除非你确定需要nil
语义(如返回 nil 表示异常)。
扩展:Map函数的用法
完整示例
package mainimport ("fmt""sync"
)func main() {// 初始化包含两个元素的 mapm := map[string]int{"Alice": 30,"Bob": 25,}// 添加新元素(当前 map 包含 3 个元素)m["Eve"] = 28// 更新现有元素(Alice 的年龄更新为 31)m["Alice"] = 31// 安全检查 Dave 是否存在(使用 exists 布尔值判断)if age, exists := m["Dave"]; exists {fmt.Println("Dave exists:", age)} else {fmt.Println("Dave not found") // 会执行这里}// 获取当前 map 长度(此时为 3,因为已添加 Eve)fmt.Println("Map length:", len(m))// 预分配容量的 map 初始化(容量 10,但可自动扩展)preAllocatedMap := make(map[string]int, 10)preAllocatedMap["test"] = 100fmt.Println("Pre-allocated map:", preAllocatedMap)// 删除 Eve 元素(map 长度变为 2)delete(m, "Eve")// 遍历当前 map(顺序随机)for name, age := range m {fmt.Printf("%s: %d\n", name, age) // 输出 Alice:31 和 Bob:25}// 并发安全控制结构var (safeMap = make(map[int]string) // 需要互斥锁保护的 mapmu sync.Mutex // 互斥锁)// 启动 goroutine 安全写入go func() {mu.Lock()safeMap[1] = "Hello" // 加锁后写入mu.Unlock()}()// 清空 map 所有元素clear(m)fmt.Println(m) // 输出空 map:map[]
}
输出:
Dave not found
Map length: 3
Pre-allocated map: map[test:100]
Alice: 31
Bob: 25
map[]