在我们学习汉字的时候,发现有一个生僻字的话,我们会使用字典。字典这种数据组织方式就是为了方便查询的操作的,那么 Go 语言中有没有这样的方式来存储数据呢?当然是有,maps。
Go maps 类似于其他编程语言的哈希表,在 Python 中称为字典,Ruby 为散列,而 JavaScript 则被称为对象,PHP 是关联数组。
不像数组和切片,maps 的主要优点是它们可以使用任何数据类型作为索引,在这种情况下称为映射键或 key。
尽管 Go maps 不排除任何数据类型作为键,但要用作键的数据类型必须具有可比性,这意味着 Go 编译器必须能够区分一个键和另一个键,或者简单地说 ,映射的键必须支持 ==
运算符。
映射
映射也是 Go 内置的数据结构,用于存储键值对的无序集合。
也被称为关联数组、哈希表或字典。类似于查字典, 映射用于通过关键键查找关联的值。键是唯一的,键对应的值可以通过键来获取、更新或删除。 可以利用映射来检测一个键是否已经存在。值存储后,可以通过引用相关的键来调用映射的值。
映射的定义
以下是 Go 中映射的定义:
var x map[string] int
映射类型由关键字 map 表示,后跟括号中的键类型,最后是值类型。你可以创建一个空 map,并用 string 作为键,int 作为值,在 go 中可以用 make()
函数创建:
myMap = make(map[string] int)
或者指定一个 预先分配的空间,就像分配切片的容量一样:
myMap := make(map[int]string, 18)
这里的 string
就是键类型, int
就是值的类型,我们也可以按如下方式创建:
myMap := make(map[string]int) myMap["小王"] = 17 myMap["小李"] = 19 myMap["老王"] = 51
也可以使用 map 字面量来创建一个带初始化键值对元素的映射::
myClass := map[string]int { "小王": 17, "小李": 19, "老张": 51, }
映射取值
类似于数组和切片,同样可以使用中括号 []
来访问映射,然后我们可以通过 myClass["小李"]
和 myClass["老张"]
来取值,将会分别得到 18 和 50 的结果。
package main import "fmt" func main() { my_map := make(map[string]int) // 创建一个从string到int的 my_map["小李"] = 18 my_map["老张"] = 50 fmt.Println(my_map) fmt.Printf("老张今年%d岁.", my_map["老张"]) }
运行该代码,结果为:
map[小李:18 老张:50] 老张今年50岁.
如果程序中访问的键并不存在于 myClass 中,那么 Go 将会根据值的类型返回相应的零值作为结果,比如 myClass["张三"]
就会返回结果为“0”。
Tips: 正因为,当我们想尝试获取 maps 中不存在的键的值,最终会得到零,导致我们无法确定结果实际上是 0,还是因为没有这个值导致的值为 0。所以在 Go 语言中有 _, ok
的用法。
_, ok := myClass["张三"] if ok { fmt.Println("张三存在~") } else { fmt.Println("张三不存在!") }
这个 ok 也不是必须这样命名,可以设置为其他非关键字 命名法,比如 age, found := myClass["张三"]。
映射遍历
for key, value := range myClass { fmt.Println(key, value) }
映射删除
可以使用内置函数 delete 来从字典中根据键移除一个元素:
delete(ages, "老张") // 删除 ages["老张"]
映射排序
映射中元素的迭代顺序是不固定的,不同的实现方法会使用不同的散列算法,得到不同的元素顺序。如果需要按照某种顺序来遍历映射中的元素,可以显式的给键排序。
像上述的例子中,键是字符串类型,可以使用 sort 包中的 Strings 函数来进行键的排序:
package main import ( "fmt" "sort" ) func main() { var names []string ages := map[string]int{ "小李": 18, "老张": 30, "老罗": 45, } for name, age := range ages { fmt.Println(name, age) } fmt.Println("--- 排序后 ---") for name := range ages { names = append(names, name) } sort.Strings(names) for _, name := range names { fmt.Printf("%s\t%d\n", name, ages[name]) } }
运行后,得到结果:
老罗 45 小李 18 老张 30 --- 排序后 --- 小李 18 老张 30 老罗 45
映射是引用类型
映射也是引用类型。 将映射分配给新变量时,它们都引用相同的底层数据结构。 因此,一个变量更改,对另一个变量也会被相应的更改。 例子如下:
package main import ( "fmt" ) func main() { var my_map = map[int]string{ 64: "new Zealand", 3: "Russia", 44: "UK", 61: "Australia", 81: "Japan", 91: "India", } fmt.Println("初始 map: ", my_map) // 赋值给新 map new_map := my_map // 新 map 中进行添加 new_map[86] = "China" new_map[33] = "France" fmt.Println("new_map: ", new_map) fmt.Println("修改后的初始 map: \n", my_map) }
运行结果:
初始 map: map[3:Russia 44:UK 61:Australia 64:new Zealand 81:Japan 91:India] new_map: map[3:Russia 33:France 44:UK 61:Australia 64:new Zealand 81:Japan 86:China 91:India] 修改后的初始 map: map[3:Russia 33:France 44:UK 61:Australia 64:new Zealand 81:Japan 86:China 91:India]
总结
映射比切片和数组更通用,但这种灵活性是有代价的:实现 Go 映射需要额外的空间。 映射非常方便,可以存储多种不同类型的数据,尤其当我们需要追求查询速度的时候,就该想到它。
映射是 Go 中键值对的无序集合。键在映射中是唯一的,但值可能不是。它被广泛使用,因为它提供了可以在键的帮助下检索、更新或删除的快速查找和值。
映射也称为关联数组、哈希表或字典。在 Go 中,您可以使用内置的 make() 函数初始化映射。它就会返回一个已初始化并可随时使用的映射。
可以使用 Go 编程语言中 for...range 循环的范围形式迭代映射。
在 Golang 中,映射是一个无序集合,因此,不能保证每次迭代时映射的迭代顺序都相同。因此,如果您多次运行任何程序,您将获得不同顺序的结果。
Maps 是 Golang 中的引用类型。 将映射分配给新变量时,它们都引用相同的底层数据结构。 因此,一个变量所做的更改,另一个变量相应会被更改。