Golang 复合数据类型:映射

映射(map)

映射是一种数据结构,用于存储一系列无序的键值对,映射基于键来存值,能够快速检索数据。键就像索引一样,指向与该键关联的值。

映射的实现

Go语言中 map 的底层是哈希(hash)表实现的,它的源码在 $GOROOT/src/pkg/runtime/hashmap.go/ 可以查看它的实现细节。Go语言的 map 是一个数组列表,不是像 C++ 一样使用红黑树,与传统的 hashmap 一样,Go语言的 map 由一个个 bucket 组成。在这个数组列表中的每一个元素都被称为 bucket 的结构体,每个 bucket 可以保存为 8 个键值对,所有元素将被 hash 算法填入到数组的 bucket 中;当 bucket 填满后,会使用一个 overflow 指针来扩展一个 bucket ,从而形成链表,以此解决 hash 冲突的问题。

映射的创建和初始化

  • 使用make函数创建映射
  • 使用映射字面量(常用)
func main(){
    //使用make函数创建映射
    a1 := make(map[string]int)
    fmt.Println(a1)  //打印零值
    //使用映射字面量初始化
    a1 = map[string]int{"Cat":1,"Dog":2,"Bee":3}
    fmt.Println(a1)
}

/*
map[]
map[Bee:3 Cat:1 Dog:2]
*/

使用映射字面量初始化是常用的方法,映射的初始长度会根据初始化时指定的键值对的数量来确定。映射的键可以是任何类型的值,内置类型或者结构类型都可以,不过需要确定这个值可以用 == 比较运算符做比较。需要注意的是,切片函数以及包含切片结构类型由于具有引用语义,所以这些数据类型都不能用作映射的键,编译器会报错:

//创建一个映射,使用字符串切片作为映射的键
func main(){
    dict := map[[]string]int{}
    fmt.Println(dict)
}

/*
command-line-arguments
invalid map key type []string
*/

但是可以使用切片作为映射的值

//创建一个映射,使用字符串切片作为映射的值
func main(){
    dict := map[int][]string{}
    fmt.Println(dict)
}

/*
map[]
*/

映射的使用

  • 元素赋值

    通过指定适当类型的键并给这个键赋值就完成了映射的键值对赋值。

    func main(){
        //使用空字面量创建初始化后的空映射
        dict := map[string]int{}
        fmt.Println(dict)
        //映射赋值
        dict["Star"] = 1
        fmt.Println(dict)
        //使用make函数创建初始化后的空映射
        dict1 := make(map[string]int)
        fmt.Println(dict1)
        //映射赋值
        dict1["Moon"] = 2
        fmt.Println(dict1)
    }
    
    /*
    map[]
    map[Star:1]
    map[]
    map[Moon:2]
    */

    声明一个未初始化的映射可以产生值为 nil 的映射。nil 映射不能用于存储键值对,否则编译器会抛出panic异常

    func main(){
        //创建一个未初始化的空映射,值为 nil 
        var dict map[string]int
        //nil 映射赋值
        dict["Star"] = 1
        fmt.Println(dict)
    }
    
    /*
    panic: assignment to entry in nil map
    */
    

    映射的查找和遍历

    映射取值有两种方式:

    • 先获取对应键的值,再判断是否存在,若存在则打印值,若不存在则什么也不做
    • 先取取对应键的值,再判断值是否为零值 (如果非空,说明存在;如果为空,说明不存在)
    func main() {
        //使用字面量创建初始化后的映射
        dict := map[string]int{"Cat": 1, "Dog": 2, "Bee": 3}
        //方法一:映射取值,获得键对应的值
        value, exists := dict["Dog"]
        if exists {
            fmt.Println(value)
        }
        //方法二:先取对应键的值,再判断值是否为零值 如果非空,说明存在;如果为空,说明不存在
        value = dict["Cat"]
             if value != 0 {
            fmt.Println(value)
        }
    }
    
    /*
    2
    1
    */

在Go语言里,通过键来索引映射时,即便这个键不存在,也会返回该值对应类型的零值。

  • 映射遍历

    func main() {
        //使用字面量创建初始化后的映射
        dict := map[string]int{"Cat": 1, "Dog": 2, "Bee": 3}
        //映射遍历
        for i,v := range dict {
            fmt.Println(i,v)
        }
    }
    
    /*
    Cat 1
    Dog 2
    Bee 3
    */
  • 元素删除

    Go语言里提供了内置函数 delete() 用于删除容器内的元素。

    func main() {
        //使用字面量创建初始化后的映射
        dict := map[string]int{"Cat": 1, "Dog": 2, "Bee": 3}
        fmt.Println(dict)
        //删除元素
        delete(dict,"Cat")
        fmt.Println(dict)
    
    }
    
    /*
    map[Bee:3 Cat:1 Dog:2]
    map[Bee:3 Dog:2]
    */

将映射传递给函数

在函数间传递映射并不会制造出该映射的一个副本

func main() {
// 创建一个映射,存储颜色以及颜色对应的十六进制代码
myColors := map[string]string{
"AliceBlue":"#f0f8ff",
"Coral":"#ff7F50",
"DarkGray":"#a9a9a9",
"ForestGreen": "#228b22",
}
// 显示映射里的所有颜色
for key, value := range myColors {
fmt.Printf("Key: %s  Value: %s\n", key, value)
}
fmt.Println()

// 调用函数来移除指定的键
removeColor(myColors, "Coral")
// 显示映射里的所有颜色
for key, value := range myColors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
}

// removeColor 将指定映射里的键删除
func removeColor(colors map[string]string, key string) {
delete(colors, key)
}

/*
Key: AliceBlue  Value: #f0f8ff
Key: Coral  Value: #ff7F50
Key: DarkGray  Value: #a9a9a9
Key: ForestGreen  Value: #228b22

Key: AliceBlue Value: #f0f8ff
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
*/

发表评论

邮箱地址不会被公开。 必填项已用*标注