映射(Map)

Golang中存储键值对的数据类型是映射,即map,由于映射这个中文词汇比较生硬,又没有其他更好的中文词,因此后面还是直接用英文map。

什么是map

Golang中map是一种用来存储键值对的数据类型,可以通过键(key)设置读取对应的值(value)

创建map

可以使用make函数来创建一个map,语法如下:

make(map[key_type]value_type)

例如我们可以创建一个名为personSalary的map来存储大家的薪水:

personSalary := make(map[string]int)

该map的key类型为string,value类型为int

map数据类型对应的零值nil。如果尝试向值为nil的map中添加元素会报错。因此一般情况下需要通过make函数来初始化一个空map。

package main

import "fmt"

func main() {
    var personSalary map[string]int
    if personSalary == nil {
        fmt.Println("map is nil. Going to make one.")
        personSalary = make(map[string]int)
    }
}

Go Playground在线运行 这里map personSalary值为nil,因此我们使用make函数创建了一个map并赋值给了personSalary。执行结果如下:

map is nil. Going to make one.

添加元素

向map内添加元素的语法跟数组添加元素一致。下面的程序向map personSalary内添加了几项元素:

package main

import "fmt"

func main() {
    personSalary := make(map[string]int)
    personSalary["steve"] = 12000
    personSalary["jamie"] = 15000
    personSalary["mike"] = 9000
    fmt.Println("personSalary map contents:", personSalary)
}

Go Playground在线运行 该程序执行结果如下:

personSalary map contents: map[jamie:15000 mike:9000 steve:12000]

map也可以在声明时同时进行初始化。

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("personSalary map contents:", personSalary)
}

Go Playground在线运行 这里声明了map personSalary,并同时初始化了两项元素。然后接着又添加了一个key为mike的元素。

Golang中不仅仅是字符串可以做为key,所有可比较类型比如布尔、整型、浮点型、复数型等等都可以作为map的key,更多关于可比较类型内容可以参考https://golang.org/ref/spec#Comparison_operators

访问元素

我们已经了解如何向map中添加元素了,现在我们看下如何访问map元素。访问map元素的语法为map[key]

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    employee := "jamie"
    fmt.Println("Salary of", employee, "is", personSalary[employee])
}

Go Playground在线运行 该程序非常简单,我们取出了jamie(key)的工资(value)并打印出来。结果如下:

Salary of jamie is 15000

如果我们访问的某个key在map中不存在,map会返回value类型对应的零值。比如对于personSalary,如果我们访问的元素不存在,则会返回int类型对应的零值0

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    fmt.Println("Salary of joe is", personSalary["joe"])
}

Go Playground在线运行 该程序执行结果如下:

Salary of joe is 0

虽然mappersonSalary中并没有元素joe,但是该程序执行并没有报错,personSalary["joe"]返回了值0

那如果我们就是想知道map中是否含有某个key怎么办呢?Golang提供了如下方式:

value, ok := map[key]

如果oktrue,表示map中含有该key,否则不含有。

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    newEmp := "joe"
    salary, ok := personSalary[newEmp]
    if ok == true {
        fmt.Println("Salary of", newEmp, "is", salary)
    } else {
        fmt.Println(newEmp, "not found")
    }
}

Go Playground在线运行 这里,由于key joe不存在,因此ok值为false。输出为:

joe not found

类似数组和切片,我们可以使用for range来迭代访问map。

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    for key, value := range personSalary {
        fmt.Printf("personSalary[%s] = %d\n", key, value)
    }
}

Go Playground在线运行 输出如下:

personSalary[steve] = 12000
personSalary[jamie] = 15000

使用for range迭代访问map有一点需要注意,每次迭代访问map元素的访问次序不保证

删除元素

从map中删除元素的语法为delete(map, key)。这里delete函数调用时无返回值。

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    fmt.Println("map before deletion", personSalary)
    delete(personSalary, "steve")
    fmt.Println("map after deletion", personSalary)
}

Go Playground在线运行 这里删除了keysteve的元素,输出如下:

map before deletion map[jamie:15000 steve:12000]
map after deletion map[jamie:15000]

获取map中元素个数

len函数可以获取map中元素的个数。

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println("length is", len(personSalary))
}

Go Playground在线运行 该程序输出为length is 3

map是引用类型

Golang中,map和切片(slice)相同,都是引用类型。如果我们把一个map变量赋值给另外一个变量,这两个变量指向同一个底层存储空间,任何一个变量对内部元素进行了修改都会反映到另一个变量上。

package main

import "fmt"

func main() {
    personSalary := map[string]int {
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000   
    fmt.Println("Original person salary", personSalary)
    newPersonSalary := personSalary
    newPersonSalary["mike"] = 18000
    fmt.Println("Person salary changed", personSalary)
}

Go Playground在线运行 这里我们将personSalary赋值给了一个新变量newPersonSalary,然后通过该新变量修改了mike的值,由于这两个map指向的底层数据存储空间是一个,因此输出如下:

Original person salary map[jamie:15000 mike:9000 steve:12000]
Person salary changed map[jamie:15000 mike:18000 steve:12000]

这点在函数传参的时候也会同样试用,需要注意。

判断相等

两个map不能通过==运算符进行比较,==仅可以用来跟nil值进行比较。

package main

func mian() {
    map1 := map[string]int {
        "one": 1,
        "two": 2,
    }

    map2 := map1
    if map1 == map2 {

    }
}

Go Playground在线运行 该程序执行会报如下错误:

invalid operation: map1 == map2 (map can only be compared to nil)

如果我们确实像判断两个map是否相等,可以遍历两个map来判断每个键值对是否相同,以间接判断。

Last updated