首页 > 技术分享 > Golang
收藏

Go语言映射(map)入门详解

08/21 17:32
大潇博客 原创文章,转载请标明出处

1、map 介绍

1.1 什么是map

在 Go 语言中,map 是一种无序的基于 key-value 的数据结构,又称为字段或关联数组。类似其它语言的集合,在编程中比较常用

定义和使用 map,有以下步骤:

① 声明 map

② 初始化 map

③ 向 map 中添加元素(赋值)

④ 从 map 中读取数据


1.2 声明map,基本语法:

var 变量名 map[keytype]valuetype

比如:

//声明 map,key 类型是 string,value 类型是 string

var m1 map[string]string

//声明 map,key 类型是 int,value类型是 string

var m2 map[int]string

var m3 map[string]int


//声明一个二维 map

var m4 map[string]map[string]string

  • key 支持的类型:bool、int、float、string、channel、指针、接口、结构体、数组等,通常 key 为 int 或 string。注意:key 的类型不可以是 slice、map、function,因为这几个不能使用“==”来判断
  • valuetype 和 keytype 基本一样,通过为 int、float、string、map、struct


2、map 的使用

2.1 初始化map:

Go 语言中的 map 是引用类型,如上面那样,只声明 map 并不会分配内存,必须使用 make 初始化后,才能赋值和使用(官方对 make 的描述为:内建函数 make 分配并初始化一个类型为切片、映射、或通道的对象)。map 初始化举例:

//声明 map

var m1 map[string]string

//使用 make 初始化 map

m1 = make(map[string]string)


//使用类型推导直接声明并初始化

m2 := make(map[int]string)

注:make 时可以设置容量(选填参数),map 的容量达到后,继续向 map 增加 key-value,map 会自动扩容,并不会发生 panic,也就是说map 能动态增的增加键值对。


2.2 map赋值

map 的“声明、初始化、赋值”有多种方式,可以先声明并初始化,然后再赋值,也可以使用类型推导直接声明初始化并赋值

//方式一:声明 map

var m1map[string]string

//使用 make 给 map 分配数据空间

m1 = make(map[string]string)

//赋值,注意key和value必须使用双引号,int类型无需引号

m1["no1"] = "Alice"

m1["no2"] = "Bob"

fmt.Println(m1)


//方式二:使用类型推导直接声明并初始化map

m2 := make(map[int]string)

m2[1] = "Audi"

m2[3] = "Benz"

m2[5] = "BMW"

fmt.Println("m2 =", m2)

//map使用 for-range 遍历

for k, v := range m2 {

fmt.Println("k=", k, "v=", v)

}


//方式三:使用类型推导声明初始化map并赋值

m3 := map[string]string{

"no1": "Tom",

"no2": "Jack",//此种格式最后必须加逗号

}

m3["no3"] = "Bob" //可以继续赋值

fmt.Println(m3)


2.3 多维map

map 内可以嵌套 map,注意 map 内部的 map,每次都需要 make 初始化后,才能赋值

//声明并初始化二维map

m3 := make(map[string]map[string]string)

m3["no1"] = make(map[string]string) //每次赋值需要再次 make

m3["no1"]["name"] = "Alice"

m3["no1"]["age"] = "30"

m3["no2"] = make(map[string]string) //每次赋值需要再次 make

m3["no2"]["name"] = "Bob"

fmt.Println("m3 =", m3)


//二维map,直接赋值

m4 := map[string]map[string]string{

"no1": map[string]string{"name": "Alice", "age": "30"}, //此种格式,map内部的最后一个元素结尾不用加逗号

"no2": map[string]string{

"name": "Bob",

"age": "20", //此种格式最后必须加逗号

}, //此处逗号不能省略

}

m4["no3"] = make(map[string]string)

m4["no3"]["name"] = "Tom"

m4["no4"] = map[string]string{

"name": "Jack",

}

fmt.Println("m4 =", m4)


//三维map案例

m5 := map[string]map[string]map[string]string{

"no1": map[string]map[string]string{ //map中的map需要再次初始化

"user1": map[string]string{"name": "Alice", "age": "30"}, //map中的map需要再次初始化

"user2": map[string]string{"name": "Bob", "age": "20"},

},

"no2": map[string]map[string]string{

"school": map[string]string{"name": "PKU"},

},

}

fmt.Println("m5 =", m5)


3、map 结合其它类型

3.1 map添加 struct 结构体

map 的 value 也经常使用 struct 类型,更适合管理复杂的数据

//定义结构体

type Val struct {

field string

name string

require bool

}

func main(){

//map 添加结构体

m7 := map[int]Val{

0: Val{"aa", "bb", true},

1: Val{"cc", "dd", false},

2: Val{"ee", "ff", true},

}

fmt.Println("m7 =", 7)

}


3.2 map切片 slice

切片的数据类型如果是 map,可以称之为 map 切片,map切片中的 map 个数可以动态变化

//方式一:声明map切片

var m6 []map[string]string

//初始化map切片,make时必须声明切片长度(切片超出此长度会报错)

m6 = make([]map[string]string, 1)

//切片内的map需要再次make初始化

m6[0] = make(map[string]string)

//赋值

m6[0]["name"] = "Alice"

m6[0]["age"] = "30"

//m6[1] = map[string]string{"name": "Jack"} //上面make时切片长度设置为1,此处超出切片长度,编译时报错提示:index out of range [...] with length ...


方式二:make初始化并赋值

var m6 []map[string]string

m6 = make([]map[string]string, 0) //默认给0,超出长度自动延伸,有性能损耗

m6[0] = map[string]string{"name": "Bob", "age": "20"}

m6[1] = map[string]string{

"name": "John",

"age": "30",

}


方式三:更简单的使用方式

var m6[]map[string]string

m6 = []map[string]string {

{"name": "**", "type": "**"},

{"name": "**", "type": "**"},

{"name": "**", "type": "**"},

{"name": "**", "type": "**"},

}

fmt.Println("m6 =", m6)


3.3 map接口 interface{}

接口(interface)是 Go 语言中的一种类型,它可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。

//方式一:声明 map

var m map[string]interface{}

//初始化

m = make(map[string]interface{})

m["name"] = "Alice"

m["age"] = "30"

m["gerden"] = "man"

fmt.Println("m =", m)


//方式二:类型推导

m := make(map[string]interface{})

m["name"] = "Alice"

fmt.Println("m =", m)


//方式三:多维map。声明 interface{} 类型后,可以是多维 map

m := make(map[string]interface{})

m["no1"] = map[string]string{

"name": "Alice",

"age": "30",

}

fmt.Println("m=", m)

使用 interface{} 作为 map 的键或值类型时,需要注意以下几点:

类型安全:由于 interface{} 可以存储任何类型,这使得类型检查变得不那么严格。在运行时,需要确保正确地处理不同类型的值。例如,当从 map 中取出一个值并尝试将其转换为特定类型时,需要使用类型断言或类型转换,不断言会提示错误:...variable of type interface{}

性能考虑:使用 interface{} 会导致运行时类型检查,这可能会比直接使用具体类型慢一些。因此,在性能敏感的应用中,应谨慎使用。

//myMap是map[string]interface{}类型

myMap := make(map[string]interface{})

myMap["one"] = 1

myMap["two"] = 2

value, ok := myMap["two"].(int) //断言,由于Go不确定interface{}中存了什么类型,所以需要做判断,尝试将 "two" 的值转换为int类型

if ok {

fmt.Println("value: ", value) //转换成功,打印值

} else {

fmt.Println("failed") //转换失败,打印错误信息

}

更详细的案例,从接口中拿到 json 数据,转换为 map

//创建 map[],给 map[] 申请一块内存空间

var result map[string]interface{} //使用 interface{} 来存储任意类型的字段值

//body 是接口返回的 json 串,多维 map,通过把 json.Unmarshal 方法把 json 数据转为 map 无序 key-value 键值对

err := json.Unmarshal([]byte(body), &result)

if err != nil {

panic(err)

}

//处理结果

if result["result"] == "OK" {

//result["data"]是多维的,类型是切片:[]interface{}

for _, v := range result["data"].([]interface{}) {

//外层是切片 []interface{},由于 interface{} 的值非常广泛,所以这里需要断言(判断v的值是什么类型,这里的v是map[string]interface{})

tmp, ok := v.(map[string]interface{}) //等价于这种写法 if tmp,ok := v.(map[string]interface{});ok {} ,判断 v 的类型

if ok {

//map[string]interface{} 内部的字段,全部需要断言(接口返回的参数,Go需要强制数据类型,所以都需要断言)

roomId, rok := tmp["roomId"].(string)

liveStatus, lok := tmp["liveStatus"].(float64)

if rok && lok {

data[roomId]["live_status"] = strconv.FormatInt(int64(liveStatus), 10)

}

}

}

} else {

panic(result["reason"].(string))

}


4、map 的使用细节

4.1 获取map长度

获取 map 长度,使用 go 内置的 len 函数,无需引入其它包

m := map[string]string{

"3": "Alice",

"5": "Bob",

"8": "Tom",

}

fmt.Println("m lenth = ", len(m))


4.2 map的增删改查操作

① map 的增加和更新,如果 key 没有就是增加,如果 key 存在就是修改

map["key"] = value  //如果 key 没有就是增加,如果 key 存在就是修改

比如:

//声明map

var m1 map[string]string

//使用make给map分配数据空间

m1 = make(map[string]string)

//赋值

m1["no1"] = "Australia"

m1["no2"] = "Canada"

m1["no1"] = "Japan" //no1已存在,此处将覆盖原来的值

fmt.Println("m1[\"no1\"] =", m1["no1"])

② map删除,使用 delete 函数,存在即删除,map 为 nil 或 key 不存在,不进行任何操作

delete(map, "key") //delete是一个内置函数,如果key存在,就会删除对应的 key-value,如果key不存在,不进行操作,也不会报错

比如:

m := map[string]string{

"no1": "Tom",

"no2": "Jack",

}

delete(m, "no1") //删除 m 中的 no1

批量删除:

Go 并没有提供批量删除 map 的函数,只能对 map 遍历后逐个删除。若要将 map 中的元素全部删除,可以 make 一个新的 map,将原来的 map 进行替换,原来的 map 将成为垃圾被 gc 回收

③ map查找

m  := map[string]string{

"name": "Bob",

"Age": 30",

}

val, flag := m[name]  //查找 name 是否存在于m的key中


4.3 map排序

Go语言中的 map 是无序的,每次遍历,得到的输出可能都不同,且 Golang 中没有一个专门的方法针对 map 的 key 进行排序,如果想对 map 进行排序,需要进行以下三步:

① 遍历 map,将 map 的 key 值存入一个切片

② 对切片进行排序

③ 遍历切片,输出对应的 map 的值

//引入sort包,其内部有排序函数

import "sort"

//使用类型推导创建map并赋值

m := map[int]string{

0: "Alice",

1: "Tom",

2: "Brown",

}

//创建一个切片

var keys []int

//遍历map,获取map的key

for k, _ := range m {

//将map的key添加到切片中

keys = append(keys, k)

}

//使用 sort 包的 Ints 函数对切片排序

sort.Ints(keys)

fmt.Println(keys)


4.4 map的传递类型

在 Go 语言中,map 是引用类型,这意味着将一个 map 赋值给另一个变量,或者作为参数传递给函数时,实际上传递的是 map 的引用,而不是 map 的副本。所以在函数内部对 map 所做的任何修改都会影响到原始的 map。

通俗来讲,就是将map赋值给另一个变量后,增删改原map或新map内的值,两个map都会发生变化,比如下面的案例:

对于把 map 赋值给另一变量的需求,建议创建一个新 map,遍历旧 map 对新 map 进行逐个赋值,比如:


5、map使用总结:

① map 在使用前一定要 make

② map 的 key 不能重复,如果重复了,将会覆盖前面的值,以最后这个 key-value 为准

③ map 的 value 是可以相同的

④ map 的 key-value 是无序的,其每次的输出顺序是随机的

⑤ map 中的 key-value类型一旦声明,不能被改变,比如声明为 string 类型,却给了 int 类型,编译时会报错

⑥ map 中嵌套 map 时,内部的 map 赋值时需要再次使用 make 初始化


打赏

上一篇:Go语言slice切片

下一篇:GO设置代理

阅读排行

大家都在搜

博客维护不易,感谢你的肯定
扫码打赏,建议金额1-10元
  • 15601023311