ZetCode

Golang make 函数

最后修改时间 2025 年 5 月 8 日

本教程将介绍如何在 Go 中使用内置的 make 函数。我们将通过实际示例介绍 slice、map 和 channel 的创建。

make 函数用于分配和初始化 slice、map 或 channel 类型的对象。与返回指针的 new 不同,make 返回一个已初始化并可直接使用的值。

在 Go 中,make 对于创建具有特定初始容量的复杂数据结构至关重要。它确保了内部状态的正确初始化。

使用 make 创建 slice

make 最简单的用法是创建具有指定长度和容量的 slice。此示例演示了基本的 slice 创建。
注意: 容量参数是可选的。

basic_slice.go
package main

import "fmt"

func main() {
    // Create slice with length 3 and capacity 5
    nums := make([]int, 3, 5)
    
    fmt.Println("Slice:", nums)
    fmt.Println("Length:", len(nums))
    fmt.Println("Capacity:", cap(nums))
    
    // Append elements within capacity
    nums = append(nums, 4, 5)
    fmt.Println("After append:", nums)
    
    // Append beyond capacity
    nums = append(nums, 6)
    fmt.Println("After exceeding capacity:", nums)
}

Slice 以其长度的零值进行初始化。容量决定了底层数组何时需要重新分配。超出容量的追加会导致自动调整大小。

使用 make 创建 map

我们可以使用 make 创建具有初始容量的 map。此示例展示了 map 的创建和基本操作。

basic_map.go
package main

import "fmt"

func main() {
    // Create map with initial capacity
    userAges := make(map[string]int, 10)
    
    // Add elements
    userAges["Alice"] = 25
    userAges["Bob"] = 30
    userAges["Charlie"] = 28
    
    fmt.Println("User ages:", userAges)
    
    // Check existence
    if age, exists := userAges["Bob"]; exists {
        fmt.Println("Bob's age:", age)
    }
    
    // Delete element
    delete(userAges, "Charlie")
    fmt.Println("After deletion:", userAges)
}

容量提示通过减少重新哈希来帮助优化 map 性能。Map 会动态增长,无论其初始容量如何。可以添加、检查和删除元素。

使用 make 创建带缓冲的 channel

make 用于创建具有指定缓冲区大小的 channel。此示例展示了带缓冲 channel 的操作。

buffered_channel.go
package main

import "fmt"

func main() {
    // Create buffered channel with capacity 3
    messages := make(chan string, 3)
    
    // Send messages without blocking
    messages <- "first"
    messages <- "second"
    messages <- "third"
    
    // Receive messages
    fmt.Println(<-messages)
    fmt.Println(<-messages)
    fmt.Println(<-messages)
    
    // Close channel
    close(messages)
}

带缓冲的 channel 允许在不阻塞的情况下发送多个值。Channel 会持有值直到被接收。关闭是可选的,但在完成后建议关闭。

使用 make 和 copy 创建 slice

make 可以创建用于复制数据的 slice。此示例演示了高效的数据复制。

slice_copy.go
package main

import "fmt"

func main() {
    original := []int{1, 2, 3, 4, 5}
    
    // Create slice with exact needed capacity
    copySlice := make([]int, len(original))
    
    // Copy elements
    copied := copy(copySlice, original)
    
    fmt.Println("Original:", original)
    fmt.Println("Copied:", copySlice)
    fmt.Println("Elements copied:", copied)
    
    // Modify copy
    copySlice[0] = 100
    fmt.Println("After modification:")
    fmt.Println("Original:", original)
    fmt.Println("Copied:", copySlice)
}

创建具有确切容量的 slice 可以避免不必要的分配。copy 函数返回复制的元素数量。修改副本不会影响原始数据。

使用 make 创建用于并发访问的 map

make 可以创建与同步原语一起使用的 map。此示例展示了线程安全的 map 访问。

concurrent_map.go
package main

import (
    "fmt"
    "sync"
)

type SafeMap struct {
    sync.RWMutex
    data map[string]int
}

func NewSafeMap() *SafeMap {
    return &SafeMap{
        data: make(map[string]int),
    }
}

func (sm *SafeMap) Set(key string, value int) {
    sm.Lock()
    defer sm.Unlock()
    sm.data[key] = value
}

func (sm *SafeMap) Get(key string) (int, bool) {
    sm.RLock()
    defer sm.RUnlock()
    val, exists := sm.data[key]
    return val, exists
}

func main() {
    sm := NewSafeMap()
    
    var wg sync.WaitGroup
    
    // Concurrent writes
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", i)
            sm.Set(key, i)
        }(i)
    }
    
    wg.Wait()
    
    // Read values
    for i := 0; i < 10; i++ {
        key := fmt.Sprintf("key%d", i)
        if val, exists := sm.Get(key); exists {
            fmt.Printf("%s: %d\n", key, val)
        }
    }
}

SafeMap 包装器提供了对使用 make 创建的 map 的线程安全访问。互斥锁保护并发访问。这种模式在并发 Go 程序中很常见。

来源

Go 语言规范

本教程通过 slice、map 和 channel 创建的实际示例,介绍了 Go 中的 make 函数。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Golang 教程