做外贸网站哪家效果好,拓和科技有限公司网站,职业培训机构资质,视频网站做短视频对于C程序员出身的Gopher来说#xff0c;map类型是和切片、interface一样能让他们感受到Go语言先进性的重要语法元素。map类型也是Go语言中最常用的数据类型之一。 go 中 map 怎么表现#xff1f;
一些有关Go语言的中文教程或译本将map称为字典或哈希表#xff0c;但在这里… 对于C程序员出身的Gopher来说map类型是和切片、interface一样能让他们感受到Go语言先进性的重要语法元素。map类型也是Go语言中最常用的数据类型之一。 go 中 map 怎么表现
一些有关Go语言的中文教程或译本将map称为字典或哈希表但在这里我选择不译直接使用map。map是Go语言提供的一种抽象数据类型它表示一组无序的键值对key-value后续我们会直接使用key和value分别表示键和值。
map类型不支持“零值可用”未显式赋初值的map类型变量的零值为nil。对处于零值状态的map变量进行操作将会导致运行时panic
var m map[string]int // m nil
m[key] 1 // panic: assignment to entry in nil map简单来说就是不能不赋值只对key 赋值是不行的。
我们必须对map类型变量进行显式初始化后才能使用它。 和切片一样创建map类型变量有两种方式
一种是使用复合字面值
1使用复合字面值创建map类型变量
// $GOROOT/src/net/status.go
var statusText map[int]string{
StatusOK: OK,
StatusCreated: Created,
StatusAccepted: Accepted,
...
}一种是使用make这个预声明的内置函数。
2使用make创建map类型变量
// $GOROOT/src/net/client.go
icookies make(map[string][]*Cookie)
// $GOROOT/src/net/h2_bundle.go
http2commonLowerHeader make(map[string]string, len(common))和切片一样map也是引用类型将map类型变量作为函数参数传入不会有很大的性能损耗并且在函数内部对map变量的修改在函数外部也是可见的比如下面的例子
func foo(m map[string]int) {m[key1] 11m[key2] 12
}
func main() {
m : map[string]int{key1: 1,key2: 2,
}fmt.Println(m) // map[key1:1 key2:2]
foo(m)
fmt.Println(m) // map[key1:11 key2:12]
}map的基本操作
1. 插入数据
面对一个非nil的map类型变量我们可以向其中插入符合map类型定义的任意键值对。 Go运行时会负责map内部的内存管理因此除非是系统内存耗尽我们不用担心向map中插入数据的数量。
m : make(map[K]V)
m[k1] v1
m[k2] v2
m[k3] v3如果key已经存在于map中则该插入操作会用新值覆盖旧值
m : map[string]int {
key1 : 1,
key2 : 2,
}m[key1] 11 // 11会覆盖掉旧值1
m[key3] 3 // map[key1:11 key2:2 key3:3]2. 获取数据个数
和切片一样map也可以通过内置函数len获取当前已经存储的数据个数
m : map[string]int {
key1 : 1,
key2 : 2,
}
fmt.Println(len(m)) // 2
m[key3] 3
fmt.Println(len(m)) // 33. 查找和数据读取
map类型更多用在查找和数据读取场合。所谓查找就是判断某个key是否存在于某个map 中。我们可以使用“comma ok”惯用法来进行查找
_, ok : m[key]
if !ok {
// key不在map中
}这里我们并不关心某个key对应的value而仅仅关心某个key是否在map中因此我们 使用空标识符blank identifier忽略了可能返回的数据值而仅关心ok的值是否为 true表示在map中。
如果要读取key对应的value的值我们可能会写出下面这样的代码
m : map[string]int
m[key1] 1
m[key2] 2
v : m[key1]
fmt.Println(v) // 1
v m[key3]
fmt.Println(v) // 0上面的代码在key存在于map中如“key1”的情况下是没有问题的。但是如果key不 存在于map中如“key3”我们看到v仍然被赋予了一个“合法”值0这个值是value 类型int的零值。在这样的情况下我们无法判定这个0是“key3”对应的值还是 因“key3”不存在而返回的零值。为此我们还需要借助“comma ok”惯用法 m : map[string]int
v, ok : m[key]
if !ok {
// key不在map中
}
fmt.Println(v)我们需要通过ok的值来判定key是否存在于map中。只有当ok true时所获得的 value值才是我们所需要的。综上Go语言的一个最佳实践是总是使用“comma ok”惯用法读取map中的值。
4. 删除数据
我们借助内置函数delete从map中删除数据
m : map[string]int {
key1 : 1,
key2 : 2,
}
fmt.Println(m) // map[key1:1 key2:2]
delete(m, key2)
fmt.Println(m) // map[key1:1]注意即便要删除的数据在map中不存在delete也不会导致panic。
5. 遍历数据
我们可以像对待切片那样通过for range语句对map中的数据进行遍历
func main() {
m : map[int]int{
1: 11,2: 12,
3: 13,
}
fmt.Printf({ )
for k, v : range m {
fmt.Printf([%d, %d] , k, v)
}
fmt.Printf(}\n)
}我们看到对同一map做多次遍历遍历的元素次序并不相同。这是因为Go运行时在初始 化map迭代器时对起始位置做了随机处理。因此千万不要依赖遍历map所得到的元素次序。
如果你需要一个稳定的遍历次序那么一个比较通用的做法是使用另一种数据结构来 按需要的次序保存key比如切片
import fmt
func doIteration(sl []int, m map[int]int) {
fmt.Printf({ )
for _, k : range sl { // 按切片中的元素次序迭代
v, ok : m[k]
if !ok {
continue
}
fmt.Printf([%d, %d] , k, v)
}
fmt.Printf(}\n)
}
func main() {
var sl []int
m : map[int]int{
1: 11,
2: 12,
3: 13,
}
for k, _ : range m {
sl append(sl, k) // 将元素按初始次序保存在切片中
}
for i : 0; i 3; i {
doIteration(sl, m)
}
}
$go run map_stable_iterate.go
{ [1, 11] [2, 12] [3, 13] }
{ [1, 11] [2, 12] [3, 13] }
{ [1, 11] [2, 12] [3, 13] }