Go 语言系列教程(七) : Map深入解析

前言

Map 哈希表是一种巧妙并且实用的数据结构。它是一个无序的key/value对的集合,其中所有的key都是不同的,在Go语言中,map类型可以写为map[K]V ,key和value之间可以是不同的数据类型。其中K对应的key必须是支持==比较运算符的数据类型 ,必须要申请空间,所有的引用类型都要这么做.

补充:

### 那些数据类型可以比较 即 那些数据类型可以作为map的key
基本类型---都可以比较
数组/struct---如果其元素类型可以比较,则数组/struct是可以比较的。即两个长度相同且对应位置元素都相等的数组/struct是相等的。
切片---仅可以与nil比较(这个我们上一节详细探讨过)
map---仅可以与nil比较,其中K对应的key必须是支持==比较运算符的数据类型
chan---同种类型的通道可以使用==比较,指向同一个通道的引用比较值为true;通道也可以和nil比较

Map

map 是一种引用类型,初值是nil,定义时必须用make来创建,否则会报错

Map的两种创建方法及普通操作

  • 内置的make函数可以创建一个map
	ages := make(map[string]int)
  • 我们也可以用map字面值的语法创建map,同时还可以指定一些最初的key/value
	ages := map[string]int{
    
    
		"alice" : 14,
		"charlie" : 23,
	}

	fmt.Println(ages) // map[alice:14 charlie:23]

这相当于

	ages := make(map[string]int)
	ages["alice"] = 14
	ages["charlie"] = 23
	for k, v := range ages {
    
    
		fmt.Printf("k=%s v=%d\n", k, v)
	}
        

结果

k=alice v=14
k=charlie v=23

Map中的元素通过key对应的下标语法访问

	ages := make(map[string]int)
	ages["alice"] = 14
	ages["charlie"] = 23
	fmt.Println(ages) // map[alice:14 charlie:23]
	ages["alice"] = 29
	fmt.Println(ages) // map[alice:29 charlie:23]

使用内置的delete函数可以删除元素

	ages := make(map[string]int)
	ages["alice"] = 14
	ages["charlie"] = 23
	fmt.Println(ages)  // map[alice:14 charlie:23]
	ages["alice"] = 29
	fmt.Println(ages)  // map[alice:29 charlie:23]

	delete(ages,"alice") 	// remove element ages["alice"]
	fmt.Println(ages) // map[charlie:23]
 

好了,到这里你已经基本学会map的基本操作了。下面我们来看一下map的注意点。

Map值得注意的地方

  • 还记得我们上一节对于切片谈到过,空的切片也是安全的,也可以进行所有切片的操作我们应该一视同仁,同样map也是如此
    如果一个查找失败将返回 value类型对应的零值,例如,即使map中不存在“bob”下面的代码也可以正常工作,因为 ages[“bob”]失败时将返回0。(注意ages我已经在上面的代码初始化过了,这里截取部分代码)
	fmt.Println(ages["bob"]) // 0
	ages["bob"] = ages["bob"] + 1
	fmt.Println(ages["bob"]) // 1

而且 x+=y 和 x++ 等简短赋值语法也可以用在map上,所以上面的代码可以改写成

	ages["bob"] += 1
	//更简单可以写成
	ages["bob"]++
	fmt.Println(ages["bob"]) // 2

思考: ages[“bob”] = ages[“bob”]++ 可不可以呢? 可以参照 Go 语言系列教程(二) : Go的数据类型以及变量声明

  • 未初始化的map可以拿来操作吗?答案是除了赋值其他操作都可以。
	fmt.Println("----------未初始化的map---------")
	var names map[string]int
	fmt.Println(names == nil) // true
	fmt.Println(len(names) == 0) // true
	names["alice"] = 12 // panic: assignment to entry in nil map
	fmt.Println("----------初始化的map---------")
	sexs := make(map[string]int)
	fmt.Println(sexs == nil) // false
	fmt.Println(len(sexs) == 0) // true

Map的比较

还记得上一节我们说过切片处理和nil比较之外不能直接比较,因为其内存地址可能会变,同样想要实现map的比较我们也得借助一个循环实现

扫描二维码关注公众号,回复: 11898308 查看本文章
func equal(x, y map[string]int) bool {
    
    
	if len(x) != len(y) {
    
    
		return false
	}
	for k, xv := range x {
    
    
		if yv, ok := y[k]; !ok || yv != xv {
    
    
			return false
		}
	}
	return true
}

解释: yv, ok := y[k]; ok这个位置赋值的是操作的结果 true / false
if yv, ok := y[k]; !ok || yv != xv 这种写法也是go语言允许的,先初始化在判断。

Map的排序输出

遍历的顺序是随机的,每一次遍历的顺序都不相同。如果要按排序遍历key/value对,我 们必须显式地对key进行排序,可以使用sort包的Strings函数对字符串slice进行排序。下面是 常见的处理方式

package main

import (
	"fmt"
	"sort"
)

func main() {
    
    

	ages := map[string]int{
    
    }
	ages["charlie"] = 23
	ages["jack"] = 16
	ages["tom"] = 19
	ages["yom"] = 27
	ages["bili"] = 20
	ages["alice"] = 14
	var names []string //声明一个字符串切片,存储map的key值
	for name := range ages {
    
    
		names = append(names, name)
	}
	sort.Strings(names) //根据map的key排序
	for _, name := range names {
    
    
		fmt.Printf("%s\t%d\n", name, ages[name])
	}

}

结果

alice	14
bili	20
charlie	23
jack	16
tom	19
yom	27

Map的并发

Go中的map在并发读的时候没问题,但是并发写就不行了(线程不安全),会发生竞态问题。

所以有一个叫sync.Map的封装数据结构供大家使用,简单用法如下:

  • 定义和存储
	var scene sync.Map
	scene.Store("name", "coding3min")
	scene.Store("age", 11)
  • 取值
	v, ok := scene.Load("name")
	if ok {
    
    
		fmt.Println(v)
	}
	v, ok = scene.Load("age")
	if ok {
    
    
		fmt.Println(v)
	}

结果

coding3min
11
  • 删除和遍历
	scene.Delete("age")

	scene.Range(func(key, value interface{
    
    }) bool {
    
    
		fmt.Println("key:",key,",value:",value)
		return true
	})

结果

key: name ,value: coding3min

----科技臻于完美,则使用愈发简单,复杂的东西都会被封装在使用者看不到的地方。

生活果然比小说精彩,因为生活压根不讲合理性!

猜你喜欢

转载自blog.csdn.net/qq_37806753/article/details/108755725