1、概述

map(映射)是存储一系列无序的key/value对,通过key来对value进行操作(增、删、改、查)。
映射的key只能为可使用==运算符的值类型(字符串、数字、布尔、数组),value可以为任意类型

map的设计也被称为The dictionary problem,它的任务是设计一种数据结构用来维护一个集合的数据,并且可以同时对集合进行增删查改的操作

Go里的map用于存放key/value对,在其它地方常称为hashdictionary、关联数组,这几种称呼都是对同一种数据结构的不同称呼,它们都用于将key经过hash函数处理,然后映射到value,实现一一对应的关系

映射是存储一系列无序的key/value对,通过key来对value进行操作(增、删、改、查)

2、声明

mapkey至少可以有==、!=运算,值可以为整数、字符串、数组

value可以是任意类型

map声明需要指定组成元素keyvalue的类型,在声明后,会被初始化为nil,表示暂不存在的映射0

var scores map[string]int  // nil映射,光声明map类型但是没有初始化
fmt.Printf("%T %#v\n", scores, scores)
fmt.Println(scores == nil)  // true

关于nil map和空map

map是不做任何赋值的map

a := map[int]string

nil map,它将不会做任何初始化,不会指向任何数据结构

var a map[int]string

如何map没初始化,直接赋值会报空指针

var a map[int]string
var b []string
fmt.Printf("%p, %p\n", a, b)
// a[0] = "a"
// b[0] = "a"

a = map[int]string{0: "a"}
b = []string{"a"}
fmt.Printf("%p, %p\n", a, b)

所以,map类型实际上就是一个指针, 具体为*hmap

3、初始化

a) 使用字面量初始化并赋值map[ktype]vtype{k1:v1, k2:v2, …, kn:vn}
b) 使用字面量初始化空映射map[ktype]vtype{ }
c) 使用make函数初始化
make(map[ktype]vtype),通过make函数创建映射,指定容量

//scores = map[string]int{}
scores = map[string]int{"a": 8, "b": 9, "c":10}
fmt.Println(scores)
//scores = make(map[string]int, 8)
// 声明map的同时完成初始化
b := map[int]bool{
    1: true,
    2: false,
}

4、操作

4.1 获取元素的数量

使用len函数获取映射元素的数量

4.2 访问

当访问key存在与映射时则返回对应的值,否则返回值类型的零值

4.3 判断key是否存在

通过key访问元素时可接收两个值,第一个值为value,第二个值为bool类型表示元
素是否存在,若存在为true,否则为false

4.4 修改和增加

使用key对映射赋值时当key存在则修改key对应的value,若key不存在则增加 keyvalue

4.5 删除

使用delete函数删除映射中已经存在的key

4.6 遍历

可通过for-range对映射中个元素进行遍历,range返回两个元素分别为映射的key
value

上述操作示例:

// 增删改查
// key
fmt.Println(scores["a"])  // 8
fmt.Println(scores["d"])  // 0  key不存在则为0,但这样不准确
// v, ok := scores["c"]
// if ok {
if v, ok := scores["c"]; ok {
  fmt.Println(v)
  fmt.Println(ok)  // false  判断某个键是否存在
}
scores["b"] = 10
fmt.Println(scores)
scores["d"] = 20
fmt.Println(scores)
// 删除
delete(scores, "c")  // 按照key删除
fmt.Println(scores)
// 获取当前映射元素的数量
fmt.Println(len(scores))
// 遍历key、value
for k, v := range scores {
  fmt.Println(k, v)  // 遍历的顺序和添加的顺序是没有任何关系的
}
// 只遍历map中的key
for k := range scores {
  fmt.Println(k)  // 遍历的顺序和添加的顺序是没有任何关系的
}
// 只遍历map中的value
for _, v := range scores {
  fmt.Println(v)
}
  • 按照某个固定顺序遍历map

默认情况下,对map遍历后都是无序的,可以通过将map中的key存到切片中,然后对切片元素排序,最终实现对map的有序遍历

package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano()) // 初始化随机数种子

	var scoreMap = make(map[string]int, 200)

	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("stu%02d", i) // 生成stu开头的字符串
		value := rand.Intn(100)          // 生成0~99的随机整数
		scoreMap[key] = value
	}
	// 正常遍历,无序
	//for k, v := range scoreMap{
	//	fmt.Println(k, v)
	//}
	// 有序遍历,按照key从小到大的顺序遍历scoreMap
	// 1、取出map中的所有key存入切片keys
	var keys = make([]string, 0, 200)   // 定义切片
	for key := range scoreMap {  // 把key添加到切片中
		keys = append(keys, key)
	}
	// 2、对切片进行排序
	sort.Strings(keys)
	// 3、按照排序后的key遍历map
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
}

4.7 多级map

多级映射:定义映射的映射

// 名字 => 映射[字符串]字符串{"1", "2", "3"}
var users map[string]map[string]string
users = map[string]map[string]string{"北京": {"1": "朝阳", "2": "东城", "3": "西城"}}
fmt.Printf("%T, %#v\n", users, users)
_, ok := users["上海"]
fmt.Println(ok)  // false
users["上海"] = map[string]string{"1": "浦东", "2": "徐汇", "3": "静安"}
fmt.Println(users)  // map[上海:map[1:浦东 2:徐汇 3:静安] 北京:map[1:朝阳 2:东城 3:西城]]
users["上海"]["3"] = "虹桥"
fmt.Println(users)  // map[上海:map[1:浦东 2:徐汇 3:虹桥] 北京:map[1:朝阳 2:东城 3:西城]]
delete(users["北京"], "1")
fmt.Println(users)  // map[上海:map[1:浦东 2:徐汇 3:虹桥] 北京:map[2:东城 3:西城]]