# 映射(Map)

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

#### 什么是map

Golang中map是一种用来存储键值对的数据类型，可以通过**键(key)**&#x6765;**设置**或**读取**对应的**值(value)**。

#### 创建map

可以使用`make`函数来创建一个map，语法如下：

```go
make(map[key_type]value_type)
```

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

```go
personSalary := make(map[string]int)
```

该map的key类型为`string`，value类型为`int`。

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

```go
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在线运行](https://play.golang.org/p/d-RoQHTmLq1) 这里map `personSalary`值为nil，因此我们使用`make`函数创建了一个map并赋值给了`personSalary`。执行结果如下：

```
map is nil. Going to make one.
```

#### 添加元素

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

```go
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在线运行](https://play.golang.org/p/RNL9qWAM8jP) 该程序执行结果如下：

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

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

```go
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在线运行](https://play.golang.org/p/VXQ0gTziDjK) 这里声明了map `personSalary`，并同时初始化了两项元素。然后接着又添加了一个key为`mike`的元素。

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

#### 访问元素

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

```go
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在线运行](https://play.golang.org/p/h1YT5xqodTY) 该程序非常简单，我们取出了`jamie`(key)的工资(value)并打印出来。结果如下：

```
Salary of jamie is 15000
```

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

```go
package main

import "fmt"

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

[Go Playground在线运行](https://play.golang.org/p/F4ZK2K_ytK3) 该程序执行结果如下：

```
Salary of joe is 0
```

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

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

```go
value, ok := map[key]
```

如果`ok`为`true`，表示map中含有该`key`，否则不含有。

```go
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在线运行](https://play.golang.org/p/MkSqzVxiLJ-) 这里，由于key `joe`不存在，因此`ok`值为false。输出为：

```
joe not found
```

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

```go
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在线运行](https://play.golang.org/p/jl78XdqTQou) 输出如下：

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

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

#### 删除元素

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

```go
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在线运行](https://play.golang.org/p/h2oP12kyavu) 这里删除了`key`为`steve`的元素，输出如下：

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

#### 获取map中元素个数

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

```go
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在线运行](https://play.golang.org/p/8h1l39SR2xV) 该程序输出为`length is 3`。

#### map是引用类型

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

```go
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在线运行](https://play.golang.org/p/eB--jeXM3rA) 这里我们将`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`值进行比较。

```go
package main

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

    map2 := map1
    if map1 == map2 {

    }
}
```

[Go Playground在线运行](https://play.golang.org/p/55BaLSpBW-s) 该程序执行会报如下错误：

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

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sunwenfei.gitbook.io/sunwenfei/golang/golang-ji-chu-jiao-cheng/qi-ta-shu-ju-lei-xing/ying-she-map.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
