jee said

Go语言Map使用指南

unclejee & AI
1132 words
6min read
Go语言Map使用指南

Go语言Map使用指南

Map是Go语言中重要的键值对数据结构,类似于其他语言中的字典或哈希表。Map提供了快速的查找、插入和删除操作。

Map创建

基本创建方式

// 方式1:使用make函数
m1 := make(map[string]int)

// 方式2:使用字面量
m2 := map[string]int{
    "apple":  5,
    "banana": 3,
    "orange": 8,
}

// 方式3:空Map
m3 := map[string]int{}

Map初始化

// 创建带容量的Map
m := make(map[string]int, 100)

// 初始化多个键值对
user := map[string]string{
    "name":  "张三",
    "email": "zhangsan@example.com",
    "city":  "北京",
}

Map操作

添加和修改

m := make(map[string]int)

// 添加键值对
m["apple"] = 5
m["banana"] = 3

// 修改已有键
m["apple"] = 10

fmt.Println(m)  // map[apple:10 banana:3]

读取值

m := map[string]int{"apple": 5, "banana": 3}

// 读取值
value := m["apple"]
fmt.Println(value)  // 5

// 检查键是否存在
value, exists := m["orange"]
if exists {
    fmt.Println("orange存在,值为:", value)
} else {
    fmt.Println("orange不存在")
}

删除元素

m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

// 删除键
delete(m, "banana")

fmt.Println(m)  // map[apple:5 orange:8]

获取长度

m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

fmt.Println(len(m))  // 3

Map遍历

遍历键值对

m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

for key, value := range m {
    fmt.Printf("%s: %d\n", key, value)
}
// 输出顺序不确定

只遍历键

m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

for key := range m {
    fmt.Println(key)
}

只遍历值

m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

for _, value := range m {
    fmt.Println(value)
}

Map排序

Go语言的Map是无序的,如果需要有序输出,需要对键进行排序。

按键排序

import "sort"

m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

// 获取所有键并排序
keys := make([]string, 0, len(m))
for key := range m {
    keys = append(keys, key)
}
sort.Strings(keys)

// 按排序后的键遍历
for _, key := range keys {
    fmt.Printf("%s: %d\n", key, m[key])
}

按值排序

type KeyValue struct {
    Key   string
    Value int
}

func main() {
    m := map[string]int{"apple": 5, "banana": 3, "orange": 8}

    // 转换为切片
    kvs := make([]KeyValue, 0, len(m))
    for key, value := range m {
        kvs = append(kvs, KeyValue{Key: key, Value: value})
    }

    // 按值排序
    sort.Slice(kvs, func(i, j int) bool {
        return kvs[i].Value > kvs[j].Value
    })

    // 输出
    for _, kv := range kvs {
        fmt.Printf("%s: %d\n", kv.Key, kv.Value)
    }
}

Map作为集合

Go语言没有内置的集合类型,但可以用Map模拟。

使用Map作为集合

// 创建集合
set := make(map[string]bool)

// 添加元素
set["apple"] = true
set["banana"] = true
set["orange"] = true

// 检查元素是否存在
if set["apple"] {
    fmt.Println("apple在集合中")
}

// 删除元素
delete(set, "banana")

// 获取集合大小
fmt.Println(len(set))  // 2

集合操作

// 并集
func union(set1, set2 map[string]bool) map[string]bool {
    result := make(map[string]bool)
    for key := range set1 {
        result[key] = true
    }
    for key := range set2 {
        result[key] = true
    }
    return result
}

// 交集
func intersection(set1, set2 map[string]bool) map[string]bool {
    result := make(map[string]bool)
    for key := range set1 {
        if set2[key] {
            result[key] = true
        }
    }
    return result
}

并发安全Map

Go语言的Map不是并发安全的,多个goroutine同时读写会导致panic。

使用sync.Map

var m sync.Map

// goroutine 1
go func() {
    m.Store("key1", "value1")
}()

// goroutine 2
go func() {
    m.Store("key2", "value2")
}()

// 读取值
if value, ok := m.Load("key1"); ok {
    fmt.Println(value)
}

使用Mutex保护

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

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

func (sm *SafeMap) Get(key string) (int, bool) {
    sm.mu.RLock()
    defer sm.mu.RUnlock()
    value, ok := sm.m[key]
    return value, ok
}

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

Map嵌套

// 嵌套Map
users := map[string]map[string]int{
    "user1": {
        "score": 100,
        "level": 5,
    },
    "user2": {
        "score": 90,
        "level": 4,
    },
}

// 访问嵌套值
fmt.Println(users["user1"]["score"])  // 100

// 修改嵌套值
users["user2"]["level"] = 10

Map与JSON

Map转JSON

import "encoding/json"

m := map[string]interface{}{
    "name":  "张三",
    "age":   30,
    "email": "zhangsan@example.com",
}

data, _ := json.Marshal(m)
fmt.Println(string(data))
// {"age":30,"email":"zhangsan@example.com","name":"张三"}

JSON转Map

jsonData := `{"name":"张三","age":30,"email":"zhangsan@example.com"}`

var m map[string]interface{}
json.Unmarshal([]byte(jsonData), &m)

fmt.Printf("%+v\n", m)
// map[age:30 email:zhangsan@example.com name:张三]

最佳实践

  1. 使用make创建Map:避免nil Map的panic
  2. 检查键是否存在:使用两个返回值检查
  3. 预分配容量:当知道大小时预分配可以提高性能
  4. 并发场景使用sync.Map:多个goroutine访问时使用并发安全Map
  5. 注意Map遍历顺序:Map的遍历顺序是不确定的

总结

Go语言的Map提供了高效的键值对存储和查找功能。通过合理使用Map,可以简化很多编程任务。掌握Map的使用对于编写高效的Go程序非常重要。