1. map的基本介绍

map是key-value数据结构,又称为字段或者关联数组。类似其它编程语言的集合,在编程中是经常使用到的

2. map的声明

1)基本语法

var map 变量名 map[keytype]valuetype

* key可以是什么类型

golang中的map的key可以是很多种类型,比如bool,数字,string,指针,channel,还可以是只包含前面几个类型的接口,结构体,数组

通常key为int、string

注意:slice,map还有function不可以,因为这几个没法用==来判断

* valuetype   可以是什么类型

valuetype的类型和key基本一样,这里就不再叙述了

通常为:数字、string、map、struct

2)map声明的举例:

var a map[string]string

var a map[string]int

var a map[int]string

var a map[string]map[string]string

注:声明是不回分配内存的,初始化需要make,分配内存后才能赋值和使用

案例演示:

package main
import (
	"fmt"
)

func main() {
	// 演示map的声明和注意事项
	var a map[string]string
	// 在使用map前,需要先make,make的作用就是给map分配数据空间
	a = make(map[string]string,10)
	
	a["no1"] = "宋江"
	a["no2"] = "武松"
	a["no1"] = "吴用"
	a["no3"] = "吴用"
	fmt.Println(a)
}

* 对上面代码的说明:

1)map在使用前一定要make

2)map的key是不能重复的,如果重复了(执行修改),则以最后这个key-value为准

3)map的value是可以相同的

4)map的key-value是无序的

5)make的内置函数数目

 3. map的使用方式

package main
import (
	"fmt"
)

func main() {
	// 如果给map赋值超出容量,会自动扩容
	// 1. 第一种使用方式
	var map1 map[string]string
	map1 = make(map[string]string,1)
	map1["no1"] = "宋江"
	map1["no2"] = "吴用"
	fmt.Println("map1", map1)
	
	// 2. 第二种使用方式
	map2 := make(map[string]string)
	map2["no1"] = "北京"
	map2["no2"] = "天津"
	map2["no3"] = "上海"
	fmt.Println("map2",map2)

	// 3. 第三种使用方式
	var map3 map[string]string = map[string]string{
		"no1" : "宋江",
		"no2" : "卢俊义",
		"no3" : "吴用",
	}
	fmt.Println("map3",map3)
}

* map使用的课堂案例

演示一个key-value 的value是map的案例

比如:我们要存放3个学生信息,每个学生有name和sex信息

package main
import (
	"fmt"
)

func main() {
	// 课堂案例:
	// 演示一个key-value 的value是map的案例
	// 比如:我们要存放3个学生信息,每个学生有name和sex信息
	// 思路: map[string]map[string]string
	studentMap := make(map[string]map[string]string)
	studentMap["stu01"] = make(map[string]string)
	studentMap["stu01"]["name"] = "mary"
	studentMap["stu01"]["sex"] = "女"
	studentMap["stu01"]["address"] = "北京"
	fmt.Println("studentMap",studentMap)

	studentMap["stu02"] = make(map[string]string)
	studentMap["stu02"]["name"] = "tom"
	studentMap["stu02"]["sex"] = "男"
	studentMap["stu02"]["address"] = "江西"
	fmt.Println("studenMap",studentMap)

	studentMap["stu03"] = make(map[string]string)
	studentMap["stu03"]["name"] = "carry"
	studentMap["stu03"]["sex"] = "男"
	studentMap["stu03"]["address"] = "广东"
	fmt.Println("student",studentMap)
}

4. map的增删改查操作

1)map的增加和更新

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

2)map删除

细节说明:

如果我们要删除map的所有key,没有一个专门的方法一次性删除,可以遍历一下key,逐个删除或者 map = make(...),make一个新的,让原来的成为垃圾,被gc回收 

3)map查找

val, ok : = map["key"]

如果这个map中存在key,那么findRes就会返回true,否则返回false

以上操作的代码如下:

package main
import (
	"fmt"
)

func main() {
	// 1. map的增加和更新
	// map["key"] = value // 如果key还没有,就是增加,如果key存在就是修改
	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"
	fmt.Println(cities)
	// 因为no3这个key已经存在,因此下面的这句话就是修改操作
	cities["no3"] = "上海~~"
	fmt.Println(cities)

	// 2. map的删除
	// delete(map,"key") delete是一个内置函数,如果key存在就删除该key-value,如果key不存在,不操作,也不报错
	delete(cities,"no1")

	// 3. map查找
	// 如果这个map中存在no2,那么findRes就会返回true,否则返回 false
	val, ok := cities["no2"]
	if ok {
		fmt.Printf("有no2 key 值为%v \n",val)
	} else {
		fmt.Printf("没有no1 key \n")
	}
}

5. map遍历

案例演示相对复杂的map遍历:该map的value又是一个map

说明:map的遍历使用for-range的结构遍历

package main
import (
	"fmt"
)
func main() {
	// 使用for-range遍历map
	cities := make(map[string]string)
	cities["no1"] = "北京"
	cities["no2"] = "天津"
	cities["no3"] = "上海"

	for k,v := range cities {
		fmt.Printf("k=%v, v=%v \n",k,v)
	}

	// 使用for-range遍历一个结构比较复杂的map
	studentMap := make(map[string]map[string]string)
	studentMap["stu01"] = make(map[string]string,3)
	studentMap["stu01"]["name"] = "tom"
	studentMap["stu01"]["sex"] = "男"
	studentMap["stu01"]["address"] = "北京长安街"

	studentMap["stu02"] = make(map[string]string)
	studentMap["stu02"]["name"] = "mary"
	studentMap["stu02"]["sex"] = "女"
	studentMap["stu02"]["address"] = "上海黄浦江"

	for k1,v1 := range studentMap {
		fmt.Println("k=",k1)
		for k2,v2 := range v1 {
			fmt.Printf("  k=%v,v=%v \n",k2,v2)
		}
	}
}

* map的长度

6. map切片

1)基本介绍

切片的数据类型如果是map,则我们称为slice of map,map切片,这样使用则map个数就可以动态变化了。

案例演示:

要求:

        使用一个map来记录monster的信息name和age,也就是说一个monster对应一个map,并且妖怪的个数可以动态的增加=> map切片

package main
import (
	"fmt"
)

func main() {
	// 演示map切片的使用
	// 要求:使用一个map来记录monster的信息 name 和 age,也就是说一个monster对应一个map,并且妖怪的个数可以动态的增加 => map切片
	// 1. 声明一个map切片
	var monsters []map[string]string
	monsters = make([]map[string]string,2) // 初始化切片
	// 2. 增加第一个妖怪的信息
	if monsters[0] == nil {
		monsters[0] = make(map[string]string)
		monsters[0]["name"] = "牛魔王"
		monsters[0]["age"] = "500"
	}

	// 增加第二个妖怪的信息
	if monsters[1] == nil {
		monsters[1] = make(map[string]string)
		monsters[1]["name"] = "玉兔精"
		monsters[1]["age"] = "400"
	}

	// 下面这个写法越界
	// if monsters[2] == nil {
	// 	monsters[2] = make(map[string]string)
	// 	monsters[2]["name"] = "猪八戒"
	// 	monsters[2]["age"] = "1000"
	// }
	// 这里我们需要使用到切片的append函数,可以进行动态增长
	newMonster := map[string]string{
		"name": "猪八戒",
		"age": "200",
	}
	monsters = append(monsters,newMonster)
	fmt.Println(monsters)
}

7. map排序

1)基本介绍

   <1> golang中没有一个专门的方法针对对map的key进行排序

  <2> golang中的map默认是无序的,注意:也不是按照添加的顺序存放的,每次遍历得到的输出可能不一样.【案例演示】

<3> golang中map的排序,是先将key进行排序,然后根据key值遍历输出即可

package main
import (
	"fmt"
	"sort"
)

// 1)golang中没有一个专门的方法针对map的key进行排序
// 2)golang中的map默认是无序的,也不是按照添加的顺序存放的,每次遍历,得到的输出可能不一样
// 3)golang中的map排序,是先将key进行排序,然后根据key值遍历输出即可

func main() {
	// map的排序
	map1 := make(map[int]int, 10)
	map1[10] = 100
	map1[1] = 13
	map1[4] = 56
	map1[8] = 90
	fmt.Println(map1)

	keys := make([]int,0)
	for k1,_ := range map1 {
		keys = append(keys,k1)
	}
	sort.Ints(keys)
	fmt.Println(keys)
	for _,v1 := range keys {
		fmt.Printf("map1[%v]=%v \n",v1,map1[v1])
	}
}

8. map的使用细节

1)map是引用类型,遵守引用类型传递的机制,在一个函数接收map,修改后,会直接修改原来的map【案例演示】

package main
import (
	"fmt"
)

func modify(map1 map[int]int) {
	map1[10] = 20
}

func main() {
	// 1. map是引用类型,遵守引用类型传递的机制,在一个函数接收map
	// 修改后,会直接修改原来的map

	map1 := make(map[int]int)
	map1[1] = 90
	map1[2] = 88
	map1[10] = 1
	map1[20] = 2
	modify(map1)
	// 看看结果  map1[10] = 20,说明map是引用类型
	fmt.Println(map1)
}

2)map的容量达到后,再向map增加元素,会自动扩容,不会发生panic,也就是说map能够动态增长 键值对(key-value)

3)map的value也经常使用struct类型,更适合管理复杂的数据(比前面value是一个map更好)

比如value为Student结构体【案例演示,因为还没学结构体,体验一下即可】

package main
import (
	"fmt"
)

type Stu struct {
	Name string
	Age byte
	Address string
}

func main() {
	students := make(map[string]Stu)
	// 创建两个学生
	stu01 := Stu{"tom", 18, "北京"}
	stu02 := Stu{"jack", 20, "上海"}
	students["no1"] = stu01
	students["no2"] = stu02

	// 遍历各个学生的信息
	for k1,v1 := range students {
		fmt.Printf("学生的编号是:%v\t",k1)
		fmt.Printf("学生的姓名是:%v\t",v1.Name)
		fmt.Printf("学生的年龄是:%v\t",v1.Age)
		fmt.Printf("学生的地址是:%v\t",v1.Address)
	}
}

首先在append之后都要判断一下是否超容,如果超容,则新数组的容量首先扩大为原来的两倍。另外还有一条规律,当扩容之后再次比较发现容量还是不够用,就只会补够到相对应的容量数(当然前提还是要偶数