零基础 go - 47(map)

0 阅读6分钟

map 是key-value 的数据结构,又称为字段或者关联数组,类似其他编程语言的集合

一、map的声明、初始化

1、map的声明、初始化

  • 声明:var 变量名 map[键类型]值类型

  • 初始化:变量名 = make(map[键类型]值类型, [初始空间大小]),空间大小可选

  • 例如:

var map1 map[string]int

map1 = make(map[string]int, 10) // 10 是可选参数,表示初始分配的空间大小,单位是元素个数

image.png

2、细节

  • map 声明的时候是不会分配内存的,不能直接使用,需要使用 make进行初始化分配空间后才能用

  • 键类型可以是很多类型,比如 bool 数字 string 指针 channel 接口 结构体 数组等。但通常更多的是 string 和 int

  • 键类型不能是 map slice 和function,因为这几个没办法用 == 来判断

  • 值类型可以是任意类型,包括 map slice function 等,但通常为 数字 string map 结构体

  • 键类型唯一,如果重复了,则以最后一个 key-value 为准

  • 值类型是可以相同的

  • map 的 key-value是无序的

3、示例


package main

import "fmt"

func main() {

    // 声明 map 但不初始化

    var map1 map[int]int

    var map2 map[string]string

    var map3 map[string]map[string]int

    var map4 map[string][2]int

    var map5 map[string]struct{

    name string

    age int

    }

    fmt.Println(map1, map2, map3, map4, map5) // 输出:map[] map[] map[] map[] map[]





    // 声明+初始化

    var m1 map[int]int

    // map[0] = 1 // 报错,没有初始化之前是不能使用的

    m1 = make(map[int]int, 10) // 使用make 进行初始化

    map[0] = 1 // 初始化后就可以使用了

    fmt.Println(m1) // 输出:map[0:1]

}

二、map的使用

  • 方式1:先声明然后使用 make进行初始化

var m1 map[int]int

m1 = make(map[int]int, 10)

m1[1] = 10

  • 方式2:使用 map字面量进行声明和初始化

m2 := map[string]int{

    "a": 1,

    "b": 2,

    "c": 3,

}

  • 方式3:使用 make 创建并初始化

m1 := make(map[int]int)

m1[0] = 10

m1[1] = 20

三、map的增删改查操作

  • 增加和更新 map:mapName[key] = value,如果 key 已经存在,则更新对应的 value;如果 key 不存在,则添加新的 key-value 对

  • 删除 map 中的元素:使用 delete(mapName, key) 函数删除 map 中的元素,如果 key 存在,则删除对应的 key-value 对;如果 key 不存在,则 delete 不执行任何操作

  • 查询 map 中的元素:使用 value, isFind = mapName[key] 来查询 map 中的元素,如果 key 存在,则返回对应的 value;

  • 如果要一次性删除,可以使用 for循环删,或者直接给 map 重新 make 一个新的空 map 来覆盖原来的 map


package main

import "fmt"

func main() {

    m1 := make(map[int]int)




    // 增加

    m1[0] = 10 // 增加 key=0 value=10

    m1[1] = 20 // 增加 key=1 value=20

    m1[2] = 30 // 增加 key=2 value=30

    fmt.Println("增加后 map:", m1) // 输出:增加后 map: map[0:10 1:20 2:30]





    // 更新 key=0 的 value

    m1[0] = 100 // 更新 key=0 的 value=100




    // 查询 key=0

    value, isFind := m1[0] // 查询 key=0

    if isFind {

        fmt.Println("key=0 存在,value=", value) // 输出:key=0 存在,value=100

    } else {

        fmt.Println("key=0 不存在")

    }




    // 删除 key=1

    delete(m1, 1) // 删除 key=1




    // 全部删除

    // 方法1:使用 for循环删除

    for k := range m1 {

        delete(m1, k)

    }

    // 方法2:直接 make 一个新的空 map 来覆盖原来的 map

    m1 = make(map[int]int)

    fmt.Println("删除后 map:", m1) // 输出:删除后 map: map[]

}

四、map的遍历、长度

  • for...range 遍历

  • len(map) 获取 map 中 key-value 对的数量


package main

import "fmt"

func main() {

    m1 := make(map[int]int)

    m1[0] = 10

    m1[1] = 20




    // 遍历 map

    for k, v := range m1 {

        fmt.Printf("key: %d, value: %d\n", k, v)

    }




    // 获取 map 的长度

    fmt.Println("map 的长度:", len(m1)) // 输出:map 的长度

}

五、map 切片

  • map 切片是指切片中的元素是 map 类型的切片

var mapSlice []map[string]int

  • map 切片的声明和初始化

package main

import "fmt"

func main{

    var mapSlice []map[string]int

    mapSlice = make([]map[string]int, 3) // 创建一个长度为3 的 map 切片




    // 遍历初始化 map 切片中的每个 map 元素

    for i := range mapSlice {

        mapSlice[i] = make(map[string]int) // 初始化每个 map 元素

    }




    // map 切片的使用

    mapSlice[0]["a"] = 1 // 给第一个 map 元素添加 key="a" value=1

    mapSlice[1]["b"] = 2 // 给第二个 map 元素添加 key="b" value=2

    mapSlice[2]["c"] = 3 // 给第三个 map 元素添加 key="c" value=3

    fmt.Println("map 切片:", mapSlice) // 输出:map

}

六、map的排序

map 是无序的,如果需要对 map 进行排序,可以将 map 的 key 提取到一个切片中,对切片进行排序,然后按照排序后的 key 来访问 map 中的元素


package main

import (

    "fmt"

    "sort"

)

func main() {

    // 1、声明初始 string类型的 map

    m1:= make(map[string]int)

    m1["c"] = 3

    m1["a"] = 1

    m1["b"] = 2

    m1["d"] = 4

    keys := make([]string, 0, len(m1)) // 创建一个切片来存储 map 的 key

    for k := range m1 {

        keys = append(keys, k) // 将 map 的 key 添加到切片中

    }

    keys = sort.Strings(keys) // 对切片中的 key 进行排序

    for _, k := range keys {

        fmt.Printf("key: %s, value: %d\n", k, m1[k]) // 按照切片中的 key 来访问 map 中的元素

    }




    // 声明一个 int 类型的 map

    m2 := make(map[int]int)

    m2[0] = 10

    m2[3] = 40

    m2[1] = 20

    var keys = []int

    for k := range m2 {

        keys = append(keys, k) // 将 map 的 key 添加到切片中

    }

    sort.Ints(keys) // 对切片中的 key 进行排序

    for _, k := range keys {

        fmt.Printf("key: %d, value: %d\n", k, m2[k]) // 按照切片中的 key 来访问 map 中的元素

    }

}

七、map的使用细节

  • map是引用类型,map变量存储的是map的地址,多个map变量可以指向同一个map,如果通过其中一个变量修改了map的内容,其他变量也会看到修改后的结果

  • map的零值是 nil,nil map 不能添加 key-value 对,但可以查询和删除 key-value 对,查询 nil map 中的任何 key 都会返回对应类型的零值,删除 nil map 中的任何 key 都不会有任何效果

  • map 的并发访问:map 不是线程安全的,如果多个 goroutine 同时读写同一个 map,可能会导致程序崩溃或者数据不一致,因此在并发场景下需要使用 sync.Mutex 或者 sync.RWMutex 来保护 map 的访问

  • map 的容量到达后,再添加匀速,并不会报错,但会自动扩容,扩容后的 map 会重新哈希所有的 key,因此在性能敏感的场景下,建议在创建 map 时就指定一个合理的初始容量,以减少扩容的次数和开销

  • map 的value经常是 struct类型


package mian

import "fmt"

func changeMap(map[int]int) {

    m[0] = 100

}

func main() {

    // map是引用类型

    m1 := map[int]int

    m1 = make(map[int]int)

    m1[0] = 10

    changeMap(m1)

    fmt.Println(m1) // 输出:map[0:100]




    // 2、nil map 不能添加 key-value 对,但可以查询和删除 key-value 对

    var m2 map[string]string // 声明一个 nil map

    fmt.Println(m2) // 输出:map[]

    // m2["a"] = "1" // 报错,不能向 nil map 中添加 key-value 对

    value := m2["a"] // 查询 nil map 中的 key "a",返回零值 ""

    fmt.Println(value) // 输出:"",map[string]string 的零值是 ""

    delete(m2, "a") // 删除 nil map 中的 key "a",不会有任何效果





    // 3、声明一个 map,键为 string 类型,值为 struct 类型

    type User struct {

        name string

        age int

    }

    m1 := make(map[string]User)

    m1["user1"] = User{name: "Alice", age: 30}

    m1["user2"] = User{name: "Bob", age: 25}

    fmt.Println("map with struct value:", m1) // 输出:map with struct value: map[user1:{Alice 30} user2:{Bob 25}]

}