查看原文
其他

Go必知必会:map详解

王中阳 王中阳
2024-08-30

文末有面经共享群

本文来自极客学院专栏,欢迎订阅:Go入门进阶实战专栏:其实学Go很简单。

在Go语言的丰富数据结构中,map 以其独特的灵活性和强大的功 能脱颖而出,它是Go中实现键值对集合的内置类型,允许开发者以一种高效且直观的方式存储和检索数据。从简单的配置管理到复杂的数据索引,在Go程序中的应用无处不在。

基础概念

在Go中,map是一个无序的键值对集合,其中键是唯一的。每个键都映射到一个值,并且可以通过键来访问对应的值;与数组和切片不同,map不是基于索引的数据结构,而是基于键的数据结构。

声明map

和切片的定义一样,我们定义map未进行初始化时,map==nil。map和切片一样,也是引用类型,不是值类型。

var m1 map[string]int  //声明
fmt.Println(m1 == nil//返回true  

没有初始化,说明没有在内存中开辟空间,没有内存地址。

初始化

map的初始化一定要先声明,再初始化;不能直接初始化,否则会报错:undefined。

var m1 map[string]int
m1 = make(map[string]int10//要估算好map的容量,避免在程序运行期间在动态扩容(动态扩容会影响程序运行效率)
m1["lucky"] = 18
m1["jason"] = 24
fmt.Println(m1)

打印结果:

注意:要估算好map的容量,避免在程序运行期间再动态扩容(动态扩容会影响程序运行效率)。

也可以使用:=简化,代表声明并初始化。

我们把上面的代码简化一下:

//简化 := 表示声明并初始化
m1 := make(map[string]int,10)
m1["lucky"] = 18
m1["jason"] = 24
fmt.Println(m1)

打印结果:

我们发现结果是一样的。

开发过程中根据场景来判断使用哪种方式来进行声明、初始化操作。

取值

取值非常简单,map[key] 的方式就能取值了,例如:

fmt.Println(m1["jason"])
fmt.Println(m1["jason1"]) //map查询不存在的key也不会报错 返回了空值

打印结果:

注意:map查询不存在的key不会报错的,返回了空值。

取值判断

上面提到了map查询不存在的key值也不会报错,而是返回了空值。

如何判断取到的值是否为空值呢?

建议map取值的时候这么写:

value,ok := m1["jason1"]
if ok {
   fmt.Println(value)
}else {
   fmt.Println("不存在")
}

打印结果:

遍历取值

取出键值对

for k, v := range m1 {
   fmt.Println(k,v)
}

打印结果:

只取键

for k := range m1 {
   fmt.Println(k)
}

打印结果:

只取值

for _, v := range m1 {
   fmt.Println(v)
}

打印结果:

删除 delete()

map的删除很简单,内置了delete函数;

不像切片移除元素那么复杂,切片移除元素只能通过分割+append的方式。

var m1 map[string]int
m1 = make(map[string]int10)
m1["lucky"] = 18
m1["jason"] = 24
fmt.Println(m1)

delete(m1,"jason")
fmt.Println(m1)

打印结果:

删除不存在的key,也不会报错。

var m1 map[string]int
m1 = make(map[string]int10)
m1["lucky"] = 18
m1["jason"] = 24
fmt.Println(m1)
fmt.Println("------")
delete(m1, "wzy")
fmt.Println(m1)

打印结果:

排序

map没有直接排序的内置方法。

下面介绍一种排序思路:我们取出map的key,存储到切片中,对key进行排序,再根据排序后的key取出map中的值。

实现思路如下:

package main

import (
   "fmt"
   "math/rand"
   "sort"
   "time"
)

func main() {
   // 初始化随机数种子
   rand.Seed(time.Now().UnixNano())

   // 声明+初始化map
   var scoreMap = make(map[string]int100)

   // 填充数据
   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([]string0200)
   for key := range scoreMap {
      keys = append(keys, key)
   }

   // 对切片进行排序
   sort.Strings(keys)
   // 按照排序后的keys遍历map
   for _, key := range keys {
      fmt.Println(key, scoreMap[key])
   }
}

注意:

  1. 定义变量时,var 和 :=不能同时存在,因为:=代表声明并初始化,同时使用会提示重复声明错误。
  2. Go生成可变的随机数需要设置随机种子,我们一般将时间戳设置为随机种子:rand.Seed(time.Now().UnixNano())

应用实战

如何生成map类型的切片

// 元素为map的切片
var s = make([]map[string]int110)
fmt.Println(s)
// 先初始化map
s[0] = make(map[string]int1)
// 赋值
s[0]["wzyage"] = 18
fmt.Printf("s类型:%T s的值:%v\n",s,s)

打印结果:

如何生成切片类型的map

// 值为切片类型的map
var m = make(map[string][]int1)
m["北京"] = []int{123//声明并初始化了
fmt.Printf("m类型:%T m的值:%v", m, m)

打印结果:

思考题

有没有办法通过make()初始化且赋值呢?

经过一番研究之后,发现是不可以的,make()的作用就是初始化为默认值,指定容量。

比如我们改写一下上面生成切片类型map的``例子``:

// 值为切片类型的map
var m = make(map[string][]int1)
// 用make初始化切片
m["北京"] = make([]int3)
fmt.Printf("m类型:%T m的值:%v", m, m)

打印结果:

总结:只能通过make()函数初始化(分配内存地址),不能使用make直接赋值。

总结

这篇文章汇总了map的知识点,包括map的声明和初始化、map的取值、map的赋值、map删除元素、map虽然不能直接排序,但是我们可以通过借助切片保存key来排序。

以及如何map和切片如何进行搭配使用。

早日上岸!

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

没准能让你能刷到自己意向公司的最新面试题呢。

感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。

点击下方文章,看看他们是怎么找到好工作的!

这些朋友赢麻了!

我们又出成绩啦!大厂Offer集锦!遥遥领先!

还有最新鲜的腾讯面经,不要错过哦!

腾讯的面试,强度拉满!

冲进腾讯了!

继续滑动看下一个
王中阳
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存