Map 的使用

173 阅读3分钟

概述

  • Go 中的 Map 是一个 key(索引)和 value(值)的无序集合,主要是查询方便,时间复杂度 O(1)
  • Go Mapvalue 的类型可以随意,但 key 的类型是有说法的!
  • 注意:Map 是无序的,而且不能保证每次打印的顺序都是相同的!

重要提示

Go 内置的 Map 不是线程安全的!当有两个线程(协程) Goroutines,同时对一个 Map 进行操作,是会报错的!

如果在并发编程的情景下,想要使用 Map,需要使用:

var syncMap sync.Map

哪些类型可作为 Map Key

可被比较的类型都可以作为 Map key!基本是固定的值,可被 Hash 的,才可以当作 key

可作为 Map key 的类型有:

  • 可比较类型可以用作映射键,包括 bool 布尔类型,int 数字类型,byte 字符类型,pointer 指针类型,channel 通道类型和 interface 接口类型。
  • 可哈希化的类型也可以用作映射键,包括 string 字符串类型和指向数组的指针类型。
  • struct 结构类型可以用作映射键,但其中包含的字段必须是可比较的和可哈希的类型。
  • array 数组类型可以用作映射键,但数组中的元素类型必须是可比较的和可哈希的类型。

不能作为 Map key 的类型有:

  • slice,因为切片是可变长度的。
  • Map,因为映射是引用类型,不能用作映射键。
  • function,因为函数是不可比较的类型。

Map 初始化与赋值

Map 的初始化方式

func main() {
    var users = map[string]string{
        "鸣人": "下忍",
        "佐助": "叛忍",
        "小樱": "中忍",
    }
    fmt.Println(users)
}
func main() {
    // 使用make初始化
    users := make(map[string]string)

    // 设置k-v
    users["鸣人"] = "下忍"
    users["佐助"] = "叛忍"
    users["小樱"] = "中忍"
    fmt.Println(users)
}

Map 容量

  • 语法:make(map[keytype]valuetype, cap)

  • 说明:

    • map 的初始容量 capacity 并非初始化必要的参数。
    • 和数组不同,map 可以根据新增的 key-value 对动态的伸缩,因此它不存在固定长度或最大限制。
    • map 增长到容量上限的时候,如果再增加新的 key-value 对,map 的大小会自动加 1
    • 出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,建议显性的标注。
    • map 是不可以使用 cap() 内置方法来查看容量的!

Map 访问元素

func main() {
    fmt.Println(users["卡卡西"])
}

Map 的 for 循环

func main() {
    // 遍历方式1
    for k, v := range users {
        fmt.Println(k, v)
    }

    // 遍历方式2
    for k := range users {
        fmt.Println(k, users[k])
    }
}

判断 Map 中是否存在元素

啰嗦写法

func main() {
    // 是否存在
    d, ok := users["卡卡西"]
    if !ok {
        fmt.Println("not find")
    } else {
        fmt.Println("find", d)
    }
}

简洁写法

func main() {
    // 这样的写法,ok变量的作用域在条件句中
    if _, ok := users["卡卡西"]; !ok {
        fmt.Println("not find")
    } else {
        fmt.Println("find")
    }
}

删除 Map 元素

func main() {
    // 即便删除不存在的k-v,也是不会报错的,可以放心使用
    delete(users, "卡卡西")
}