GO语言基础篇(十三)- map详解

这是我参与8月更文挑战的第 13 天,活动详情查看: 8月更文挑战

map

声明 & 初始化map

map是一个拥有键值对元素的无序集合。在这个集合中,键的值是唯一的,键对应的值可以通过键来获取、更新或移除。无论这个散列表有多大,这些操作基本上是通过常量时间的键比较就可以完成。

在 Go 语言中, map是散列表的引用, map的类型是map[K]V ,其中 K 和 V 是字典的键和值对应的数据类型。 map 中所有的键都拥有相同的数据类型,同时所有的值也都拥有相同的数据类型,但是键的类型和值的类型不一定相同。键的类型 K ,必须是可以通过操作符==来进行比较的数据类型,所以 map 可以检测某一个键是否已经存在。虽然浮点型是可以比较的,但是比较浮点型的相等性不是一个好主意

内置函数make可以用来创建一个map

ages := make(map[string]int)//创建一个string到int的map

//也可以使用map的字面量来新建一个带初始化键值对元素的字典
ages := map[string]int{
    "alice": 31
    "charlie": 34
}
//等价于
ages := make(map[string]int)
ages["alice"] = 31
ages["charlie"] = 34

//创建一个空的map
map[string]int{}

//使用内置函数delete来从字典中根据键移除一个元素
delete(ages, "alice")
复制代码

即使键不在map里,上边的操作也是安全的。map使用给定的键来查找元素,如果对应的元素不存在,就返回值类型的零值。比如下边这个示例中,尽管”bob”不是map的键,它也不会报错

ages["bob"] = ages["bob"] + 1 //ages["bob"]不存在,取ages值的零值(int类型的零值是0)
复制代码

为什么不可以取map元素地址

需要注意的是,map的元素不是一个变量,不可以获取它的地址

_ = &ages["bob"] // 编译错误,无法获取map元素的地址
复制代码

无法获取到map元素的地址的另一个原因是,map的增长可能会导致已有元素被重新散列到新的存储位置,这样就可能使获取到的地址无效

map的遍历

可以使用for循环(结合range)来遍历map中所有的键和对应的值

for name, age := range ages {
    fmt.Printf("%s\t%d\n", name, age)
}
复制代码

map中元素的迭代顺序是不固定的,不同的实现方法会使用不同的散列算法,得到不同的元素顺序。这种顺序是随机的,这个是有意为之的,这样可以使得程序在不同的散列算法实现下变得健壮。如果按照某种顺序来遍历map中的元素,必须显式的来给键排序

var names []string
for name := range ages {
    names = append(names, name)
}
sort.Strings(names)
for _, name := range ages { //用_来忽略第一个变量
    fmt.Println("%s\t%d\n", name, ages[name])
}
复制代码

因为在上边我们一开始就知道slice names的长度,所以直接指定一个slice的长度会更加高效

names := make([]string, 0, len(ages))
复制代码

map的零值和比较

map类型的零值是nil,也就是说没有引用任何散列表

var ages map[string]int
fmt.Println(ages == nil) // true
fmt.Println(len(ages) == 0) // true
复制代码

大多数的map操作都可以安全的在map的零值nil上执行,包括查找元素、删除元素、获取map元素个数、执行range循环,因为这和空map的行为一致。但是,向零值map中设置元素会导致错误。设置元素之前,必须初始化map

ages["carol"] = 21 //宕机,为零值map中的项赋值
复制代码

通过下标访问map总会有值,如果键在map中,会得到键对应的值;如果键不在map中,会得到map值类型的零值。在很多情况下,这种没问题,但是有时候你需要知道一个元素是否在map中

age, ok := ages["bob"]
复制代码

第二个返回值就是报告该元素是否存在

和slice一样,map不可比较。唯一的比较就是和nil做比较。为了判断两个map是否拥有相同的键和值,必须写一个循环体

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
}
复制代码

需要关注的点是,如何区分“元素不存在”和“元素存在但为零值”的情况。如果上边简单的写成xv ≠ yv,那么像下边这种情况,就会错误的报告两个map是相等的

//如果equal简单的写成xv ≠ yv的情况下,下边这种情况,它就会错误返回他们相等
equal(map[string]int{"A":0}, map[string]int{"B":42})
复制代码

参考

《Go程序设计语言》—-艾伦 A. A. 多诺万

《Go语言学习笔记》—-雨痕

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享