Golang map

// 数组、切片的问题:查找数据不方便,需要明确下标
	names := []string{"张三", "李四", "王五", "马六", "钱七"}
	fmt.Println(names[2]) // 王五

// So,有没有一种数据结构,能够帮我们快速的找到数据呢?

map,是一种无序的基于 key-value 的数据结构,其强大之处在于,可以使用 key( key,不允许重复 ),来快速索引数据

// 基本语法
   map[KeyType]ValueType
     其中,KeyType,表示键的类型;ValueType,表示键对应的值的类型

   // map 类型的变量,默认初始值为nil,需要使用 make() 函数来分配内存
      make(map[KeyType]ValueType, [cap])  // cap,表示 map 的容量,可选

需说明的是:

  • map 是引用类型,必须初始化,才能使用
    • map 作为函数参数,遵循引用类型的传递机制,修改后,会对原数据产生影响
  • map 是无序的,对其进行遍历时,无法决定返回的顺序,因为 map 是使用 hash 表来实现的

map 的创建和使用

(1)初始化:map,是引用类型,必须初始化后,才能使用。其初始化方式,主要有以下两种:

// 方式1:声明后,使用 make() 函数开辟内存空间     -  可以简写

	// 步骤一:声明:var mapName map[键的类型]值的类型
	var m1 map[string]string  // 声明一个未初始化的map,可以创建一个值为 nil 的映射
	fmt.Println(m1) // map[]  // 即,声明后的变量,默认初始值为 nil

	// nil map,不能用于存储键值对,否则,会产生运行错误
	//m1["name"] = "张三" // panic: assignment to entry in nil map

	// 步骤二:使用 make() 函数开辟内存空间,不然,就是一个 nil map
	// 基本语法:make(map[KeyType]ValueType, [cap])
	// 其中,cap,表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map时,就为其指定一个合适的容量

	m1 = make(map[string]string, 10)
	m1["name"] = "张三"
	fmt.Println(m1) // map[name:张三]

// 可以简写为:

m2 := make(map[string]string, 10)
fmt.Println(m2)
m2["name"] = "张三"
m2["age"] = "22"
fmt.Println(m2) // map[age:22 name:张三]


// 方式2:声明的时候,直接填充元素

	var m3 map[string]string = map[string]string{
		"name": "小明",
		"sex":  "男",
		"age":  "20",
	}
	fmt.Println(m3) // map[age:20 name:小明 sex:男]
	
	// 或
	m4 := map[string]string{
		"name": "李四",
		"age":  "25",
	}
	fmt.Println(m4) // map[age:25 name:李四]

(2)赋值 & 判断某个 key 是否存在

  • map 赋值,只能使用键值对的形式,没有 append 函数,因为 map 是无序的,不存在往中间添加数据的说法
    • 基本语法:map[key] = value,如果存在 key,则会覆盖对应的 value
    • 容量达到后,再给 map 填充元素,会自动扩容,并不会发生 panic,即,支持动态扩容( nil map 除外
func main() {
	colorMap := map[string]string{
		"red":  "红色",
		"blue": "蓝色",
	}
	fmt.Println(colorMap) // map[blue:蓝色 red:红色]

	// map 赋值
	colorMap["red"] = "红色的"        // 会覆盖掉之前的
	fmt.Println(colorMap["red"])   // 红色的
	fmt.Println(colorMap["green"]) //

	// So,如果不想覆盖掉之前的,或者不想查找一个根本不存在的key值
	// 可以先判断一下 key 是否存在:value, ok := map[key]
	v, ok := colorMap["red"]
	// 如果存在
	if ok {
		fmt.Println(v) // 红色的
	} else {
		// 如果不存在
	}

	// 或
	if _, ok1 := colorMap["green"]; !ok1 {
		colorMap["green"] = "绿色"
	}
	fmt.Println(colorMap) // map[blue:蓝色 green:绿色 red:红色的]
}

(3)遍历:遍历 map 时,元素顺序与添加键值对的顺序无关

func main() {
	colorMap := map[string]string{
		"red":  "红色",
		"blue": "蓝色",
	}
	fmt.Println(colorMap) // map[blue:蓝色 red:红色]

	// 遍历,使用 for range
	for k, v := range colorMap {
		fmt.Println(k, v) // 遍历 map 时,元素顺序与添加键值对的顺序无关
	}

	// 只想遍历 key
	for k := range colorMap {
		fmt.Println(k)
	}

	// 只想遍历 value
	for _, v := range colorMap {
		fmt.Println(v)
	}
}

按指定顺序遍历 map( 略 ) 」

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
	}
	// 取出map中的所有key存入切片keys
	var keys = make([]string, 0, 200)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	// 对切片进行排序
	sort.Strings(keys)
	// 按照排序后的key遍历map
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
}

(4)删除键值对:delete(map, key)

  • map,表示要删除键值对的 map
  • key,表示要删除的键值对的键
func main() {
	colorMap := map[string]string{
		"red":  "红色",
		"blue": "蓝色",
	}
	fmt.Println(colorMap) // map[blue:蓝色 red:红色]

	// 删除键值对
	delete(colorMap, "red")
	fmt.Println(colorMap) // map[blue:蓝色]
}